X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gold%2Fi386.cc;h=dd0b268e10bfbb49ee344946591614538cf9e665;hb=bc2a507e6e90fc5766bd68d368441f5b511c2ef1;hp=6dceca55732bb8697b91af8baab9828f30cfc161;hpb=93f8221c643088fab039a4934623f0be0762d777;p=deliverable%2Fbinutils-gdb.git diff --git a/gold/i386.cc b/gold/i386.cc index 6dceca5573..dd0b268e10 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -1,7 +1,6 @@ // i386.cc -- i386 target support for gold. -// Copyright 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 -// Free Software Foundation, Inc. +// Copyright (C) 2006-2019 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. @@ -48,6 +47,32 @@ namespace using namespace gold; +// A class to handle the .got.plt section. + +class Output_data_got_plt_i386 : public Output_section_data_build +{ + public: + Output_data_got_plt_i386(Layout* layout) + : Output_section_data_build(4), + layout_(layout) + { } + + protected: + // Write out the PLT data. + void + do_write(Output_file*); + + // Write to a map file. + void + do_print_to_mapfile(Mapfile* mapfile) const + { mapfile->print_output_data(this, "** GOT PLT"); } + + private: + // A pointer to the Layout class, so that we can find the .dynamic + // section when we write out the GOT PLT section. + Layout* layout_; +}; + // A class to handle the PLT data. // This is an abstract base class that handles most of the linker details // but does not know the actual contents of PLT entries. The derived @@ -59,7 +84,7 @@ class Output_data_plt_i386 : public Output_section_data typedef Output_data_reloc Reloc_section; Output_data_plt_i386(Layout*, uint64_t addralign, - Output_data_space*, Output_data_space*); + Output_data_got_plt_i386*, Output_data_space*); // Add an entry to the PLT. void @@ -202,9 +227,6 @@ class Output_data_plt_i386 : public Output_section_data unsigned int got_offset; }; - // A pointer to the Layout class, so that we can find the .dynamic - // section when we write out the GOT PLT section. - Layout* layout_; // The reloc section. Reloc_section* rel_; // The TLS_DESC relocations, if necessary. These must follow the @@ -214,7 +236,7 @@ class Output_data_plt_i386 : public Output_section_data // regular relocatoins and the TLS_DESC relocations. Reloc_section* irelative_rel_; // The .got.plt section. - Output_data_space* got_plt_; + Output_data_got_plt_i386* got_plt_; // The part of the .got.plt section used for IRELATIVE relocs. Output_data_space* got_irelative_; // The number of PLT entries. @@ -237,7 +259,7 @@ class Output_data_plt_i386_standard : public Output_data_plt_i386 { public: Output_data_plt_i386_standard(Layout* layout, - Output_data_space* got_plt, + Output_data_got_plt_i386* got_plt, Output_data_space* got_irelative) : Output_data_plt_i386(layout, plt_entry_size, got_plt, got_irelative) { } @@ -268,7 +290,7 @@ class Output_data_plt_i386_exec : public Output_data_plt_i386_standard { public: Output_data_plt_i386_exec(Layout* layout, - Output_data_space* got_plt, + Output_data_got_plt_i386* got_plt, Output_data_space* got_irelative) : Output_data_plt_i386_standard(layout, got_plt, got_irelative) { } @@ -299,7 +321,7 @@ class Output_data_plt_i386_dyn : public Output_data_plt_i386_standard { public: Output_data_plt_i386_dyn(Layout* layout, - Output_data_space* got_plt, + Output_data_got_plt_i386* got_plt, Output_data_space* got_irelative) : Output_data_plt_i386_standard(layout, got_plt, got_irelative) { } @@ -337,7 +359,7 @@ class Target_i386 : public Sized_target<32, false> : Sized_target<32, false>(info), got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL), got_tlsdesc_(NULL), global_offset_table_(NULL), rel_dyn_(NULL), - rel_irelative_(NULL), copy_relocs_(elfcpp::R_386_COPY), dynbss_(NULL), + rel_irelative_(NULL), copy_relocs_(elfcpp::R_386_COPY), got_mod_index_offset_(-1U), tls_base_symbol_defined_(false) { } @@ -407,6 +429,21 @@ class Target_i386 : public Sized_target<32, false> const unsigned char* plocal_symbols, Relocatable_relocs*); + // Scan the relocs for --emit-relocs. + void + emit_relocs_scan(Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<32, false>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + size_t local_symbol_count, + const unsigned char* plocal_syms, + Relocatable_relocs* rr); + // Emit relocations for a section. void relocate_relocs(const Relocate_info<32, false>*, @@ -415,7 +452,6 @@ class Target_i386 : public Sized_target<32, false> size_t reloc_count, Output_section* output_section, elfcpp::Elf_types<32>::Elf_Off offset_in_output_section, - const Relocatable_relocs*, unsigned char* view, elfcpp::Elf_types<32>::Elf_Addr view_address, section_size_type view_size, @@ -464,12 +500,14 @@ class Target_i386 : public Sized_target<32, false> // Return whether SYM is call to a non-split function. bool - do_is_call_to_non_split(const Symbol* sym, unsigned int) const; + do_is_call_to_non_split(const Symbol* sym, const unsigned char*, + const unsigned char*, section_size_type) const; // Adjust -fsplit-stack code which calls non-split-stack code. void do_calls_non_split(Relobj* object, unsigned int shndx, section_offset_type fnoffset, section_size_type fnsize, + const unsigned char* prelocs, size_t reloc_count, unsigned char* view, section_size_type view_size, std::string* from, std::string* to) const; @@ -507,14 +545,14 @@ class Target_i386 : public Sized_target<32, false> // This chooses the right PLT flavor for an executable or a shared object. Output_data_plt_i386* make_data_plt(Layout* layout, - Output_data_space* got_plt, + Output_data_got_plt_i386* got_plt, Output_data_space* got_irelative, bool dyn) { return this->do_make_data_plt(layout, got_plt, got_irelative, dyn); } virtual Output_data_plt_i386* do_make_data_plt(Layout* layout, - Output_data_space* got_plt, + Output_data_got_plt_i386* got_plt, Output_data_space* got_irelative, bool dyn) { @@ -612,10 +650,9 @@ class Target_i386 : public Sized_target<32, false> // Do a relocation. Return false if the caller should not issue // any warnings about this relocation. inline bool - relocate(const Relocate_info<32, false>*, Target_i386*, Output_section*, - size_t relnum, const elfcpp::Rel<32, false>&, - unsigned int r_type, const Sized_symbol<32>*, - const Symbol_value<32>*, + relocate(const Relocate_info<32, false>*, unsigned int, + Target_i386*, Output_section*, size_t, const unsigned char*, + const Sized_symbol<32>*, const Symbol_value<32>*, unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, section_size_type); @@ -632,7 +669,6 @@ class Target_i386 : public Sized_target<32, false> // Do a TLS General-Dynamic to Initial-Exec transition. inline void tls_gd_to_ie(const Relocate_info<32, false>*, size_t relnum, - Output_segment* tls_segment, const elfcpp::Rel<32, false>&, unsigned int r_type, elfcpp::Elf_types<32>::Elf_Addr value, unsigned char* view, @@ -651,7 +687,6 @@ class Target_i386 : public Sized_target<32, false> // transition. inline void tls_desc_gd_to_ie(const Relocate_info<32, false>*, size_t relnum, - Output_segment* tls_segment, const elfcpp::Rel<32, false>&, unsigned int r_type, elfcpp::Elf_types<32>::Elf_Addr value, unsigned char* view, @@ -702,12 +737,22 @@ class Target_i386 : public Sized_target<32, false> Local_dynamic_type local_dynamic_type_; }; - // A class which returns the size required for a relocation type, - // used while scanning relocs during a relocatable link. - class Relocatable_size_for_reloc + // A class for inquiring about properties of a relocation, + // used while scanning relocs during a relocatable link and + // garbage collection. + class Classify_reloc : + public gold::Default_classify_reloc { public: - unsigned int + typedef Reloc_types::Reloc Reltype; + + // Return the explicit addend of the relocation (return 0 for SHT_REL). + static elfcpp::Elf_types<32>::Elf_Swxword + get_r_addend(const Reltype*) + { return 0; } + + // Return the size of the addend of the relocation (only used for SHT_REL). + static unsigned int get_size_for_reloc(unsigned int, Relobj*); }; @@ -716,12 +761,32 @@ class Target_i386 : public Sized_target<32, false> static tls::Tls_optimization optimize_tls_reloc(bool is_final, int r_type); + // Check if relocation against this symbol is a candidate for + // conversion from + // mov foo@GOT(%reg), %reg + // to + // lea foo@GOTOFF(%reg), %reg. + static bool + can_convert_mov_to_lea(const Symbol* gsym) + { + gold_assert(gsym != NULL); + return (gsym->type() != elfcpp::STT_GNU_IFUNC + && !gsym->is_undefined () + && !gsym->is_from_dynobj() + && !gsym->is_preemptible() + && (!parameters->options().shared() + || (gsym->visibility() != elfcpp::STV_DEFAULT + && gsym->visibility() != elfcpp::STV_PROTECTED) + || parameters->options().Bsymbolic()) + && strcmp(gsym->name(), "_DYNAMIC") != 0); + } + // Get the GOT section, creating it if necessary. Output_data_got<32, false>* got_section(Symbol_table*, Layout*); // Get the GOT PLT section. - Output_data_space* + Output_data_got_plt_i386* got_plt_section() const { gold_assert(this->got_plt_ != NULL); @@ -786,9 +851,11 @@ class Target_i386 : public Sized_target<32, false> unsigned int shndx, Output_section* output_section, Symbol* sym, const elfcpp::Rel<32, false>& reloc) { + unsigned int r_type = elfcpp::elf_r_type<32>(reloc.get_r_info()); this->copy_relocs_.copy_reloc(symtab, layout, symtab->get_sized_symbol<32>(sym), - object, shndx, output_section, reloc, + object, shndx, output_section, + r_type, reloc.get_r_offset(), 0, this->rel_dyn_section(layout)); } @@ -814,7 +881,7 @@ class Target_i386 : public Sized_target<32, false> // The PLT section. Output_data_plt_i386* plt_; // The GOT PLT section. - Output_data_space* got_plt_; + Output_data_got_plt_i386* got_plt_; // The GOT section for IRELATIVE relocations. Output_data_space* got_irelative_; // The GOT section for TLSDESC relocations. @@ -827,8 +894,6 @@ class Target_i386 : public Sized_target<32, false> Reloc_section* rel_irelative_; // Relocs saved to avoid a COPY reloc. Copy_relocs copy_relocs_; - // Space for variables copied with a COPY reloc. - Output_data_space* dynbss_; // Offset of the GOT entry for the TLS module index. unsigned int got_mod_index_offset_; // True if the _TLS_MODULE_BASE_ symbol has been defined. @@ -858,7 +923,9 @@ const Target::Target_info Target_i386::i386_info = 0, // large_common_section_flags NULL, // attributes_section NULL, // attributes_vendor - "_start" // entry_symbol_name + "_start", // entry_symbol_name + 32, // hash_entry_size + elfcpp::SHT_PROGBITS, // unwind_section_type }; // Get the GOT section, creating it if necessary. @@ -888,7 +955,7 @@ Target_i386::got_section(Symbol_table* symtab, Layout* layout) | elfcpp::SHF_WRITE), this->got_, got_order, true); - this->got_plt_ = new Output_data_space(4, "** GOT PLT"); + this->got_plt_ = new Output_data_got_plt_i386(layout); layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), @@ -975,18 +1042,39 @@ Target_i386::rel_irelative_section(Layout* layout) return this->rel_irelative_; } +// Write the first three reserved words of the .got.plt section. +// The remainder of the section is written while writing the PLT +// in Output_data_plt_i386::do_write. + +void +Output_data_got_plt_i386::do_write(Output_file* of) +{ + // The first entry in the GOT is the address of the .dynamic section + // aka the PT_DYNAMIC segment. The next two entries are reserved. + // We saved space for them when we created the section in + // Target_i386::got_section. + const off_t got_file_offset = this->offset(); + gold_assert(this->data_size() >= 12); + unsigned char* const got_view = of->get_output_view(got_file_offset, 12); + Output_section* dynamic = this->layout_->dynamic_section(); + uint32_t dynamic_addr = dynamic == NULL ? 0 : dynamic->address(); + elfcpp::Swap<32, false>::writeval(got_view, dynamic_addr); + memset(got_view + 4, 0, 8); + of->write_output_view(got_file_offset, 12, got_view); +} + // 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. Output_data_plt_i386::Output_data_plt_i386(Layout* layout, uint64_t addralign, - Output_data_space* got_plt, + Output_data_got_plt_i386* got_plt, Output_data_space* got_irelative) : Output_section_data(addralign), - layout_(layout), tls_desc_rel_(NULL), - irelative_rel_(NULL), got_plt_(got_plt), got_irelative_(got_irelative), - count_(0), irelative_count_(0), global_ifuncs_(), local_ifuncs_() + tls_desc_rel_(NULL), irelative_rel_(NULL), got_plt_(got_plt), + got_irelative_(got_irelative), count_(0), irelative_count_(0), + global_ifuncs_(), local_ifuncs_() { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, @@ -1326,6 +1414,7 @@ Output_data_plt_i386::do_write(Output_file* of) const section_size_type got_size = convert_to_section_size_type(this->got_plt_->data_size() + this->got_irelative_->data_size()); + unsigned char* const got_view = of->get_output_view(got_file_offset, got_size); @@ -1337,18 +1426,9 @@ Output_data_plt_i386::do_write(Output_file* of) this->fill_first_plt_entry(pov, got_address); pov += this->get_plt_entry_size(); - unsigned char* got_pov = got_view; - - // The first entry in the GOT is the address of the .dynamic section - // aka the PT_DYNAMIC segment. The next two entries are reserved. - // We saved space for them when we created the section in - // Target_i386::got_section. - Output_section* dynamic = this->layout_->dynamic_section(); - uint32_t dynamic_addr = dynamic == NULL ? 0 : dynamic->address(); - elfcpp::Swap<32, false>::writeval(got_pov, dynamic_addr); - got_pov += 4; - memset(got_pov, 0, 8); - got_pov += 8; + // The first three entries in the GOT are reserved, and are written + // by Output_data_got_plt_i386::do_write. + unsigned char* got_pov = got_view + 12; const int rel_size = elfcpp::Elf_sizes<32>::rel_size; @@ -1486,6 +1566,8 @@ Target_i386::plt_entry_count() const unsigned int Target_i386::first_plt_entry_offset() const { + if (this->plt_ == NULL) + return 0; return this->plt_->first_plt_entry_offset(); } @@ -1494,6 +1576,8 @@ Target_i386::first_plt_entry_offset() const unsigned int Target_i386::plt_entry_size() const { + if (this->plt_ == NULL) + return 0; return this->plt_->get_plt_entry_size(); } @@ -1637,6 +1721,7 @@ Target_i386::Scan::get_reference_flags(unsigned int r_type) return Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF; case elfcpp::R_386_GOT32: + case elfcpp::R_386_GOT32X: // Absolute in GOT. return Symbol::ABSOLUTE_REF; @@ -1801,9 +1886,28 @@ Target_i386::Scan::local(Symbol_table* symtab, break; case elfcpp::R_386_GOT32: + case elfcpp::R_386_GOT32X: { - // The symbol requires a GOT entry. + // We need GOT section. Output_data_got<32, false>* got = target->got_section(symtab, layout); + + // If the relocation symbol isn't IFUNC, + // and is local, then we will convert + // mov foo@GOT(%reg), %reg + // to + // lea foo@GOTOFF(%reg), %reg + // in Relocate::relocate. + if (reloc.get_r_offset() >= 2 + && lsym.get_st_type() != elfcpp::STT_GNU_IFUNC) + { + section_size_type stype; + const unsigned char* view = object->section_contents(data_shndx, + &stype, true); + if (view[reloc.get_r_offset() - 2] == 0x8b) + break; + } + + // Otherwise, the symbol requires a GOT entry. unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); // For a STT_GNU_IFUNC symbol we want the PLT offset. That @@ -2034,6 +2138,7 @@ Target_i386::Scan::possible_function_pointer_reloc(unsigned int r_type) case elfcpp::R_386_8: case elfcpp::R_386_GOTOFF: case elfcpp::R_386_GOT32: + case elfcpp::R_386_GOT32X: { return true; } @@ -2116,7 +2221,8 @@ Target_i386::Scan::global(Symbol_table* symtab, // Make a dynamic relocation if necessary. if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))) { - if (gsym->may_need_copy_reloc()) + if (!parameters->options().output_is_position_independent() + && gsym->may_need_copy_reloc()) { target->copy_reloc(symtab, layout, object, data_shndx, output_section, gsym, reloc); @@ -2177,7 +2283,8 @@ Target_i386::Scan::global(Symbol_table* symtab, // Make a dynamic relocation if necessary. if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))) { - if (gsym->may_need_copy_reloc()) + if (parameters->options().output_is_executable() + && gsym->may_need_copy_reloc()) { target->copy_reloc(symtab, layout, object, data_shndx, output_section, gsym, reloc); @@ -2193,9 +2300,26 @@ Target_i386::Scan::global(Symbol_table* symtab, break; case elfcpp::R_386_GOT32: + case elfcpp::R_386_GOT32X: { - // The symbol requires a GOT entry. + // The symbol requires a GOT section. Output_data_got<32, false>* got = target->got_section(symtab, layout); + + // If we convert this from + // mov foo@GOT(%reg), %reg + // to + // lea foo@GOTOFF(%reg), %reg + // in Relocate::relocate, then there is nothing to do here. + if (reloc.get_r_offset() >= 2 + && Target_i386::can_convert_mov_to_lea(gsym)) + { + section_size_type stype; + const unsigned char* view = object->section_contents(data_shndx, + &stype, true); + if (view[reloc.get_r_offset() - 2] == 0x8b) + break; + } + if (gsym->final_value_is_known()) { // For a STT_GNU_IFUNC symbol we want the PLT address. @@ -2275,6 +2399,23 @@ Target_i386::Scan::global(Symbol_table* symtab, break; case elfcpp::R_386_GOTOFF: + // A GOT-relative reference must resolve locally. + if (!gsym->is_defined()) + gold_error(_("%s: relocation R_386_GOTOFF against undefined symbol %s" + " cannot be used when making a shared object"), + object->name().c_str(), gsym->name()); + else if (gsym->is_from_dynobj()) + gold_error(_("%s: relocation R_386_GOTOFF against external symbol %s" + " cannot be used when making a shared object"), + object->name().c_str(), gsym->name()); + else if (gsym->is_preemptible()) + gold_error(_("%s: relocation R_386_GOTOFF against preemptible symbol %s" + " cannot be used when making a shared object"), + object->name().c_str(), gsym->name()); + // We need a GOT section. + target->got_section(symtab, layout); + break; + case elfcpp::R_386_GOTPC: // We need a GOT section. target->got_section(symtab, layout); @@ -2469,9 +2610,7 @@ Target_i386::gc_process_relocs(Symbol_table* symtab, size_t local_symbol_count, const unsigned char* plocal_symbols) { - gold::gc_process_relocs<32, false, Target_i386, elfcpp::SHT_REL, - Target_i386::Scan, - Target_i386::Relocatable_size_for_reloc>( + gold::gc_process_relocs<32, false, Target_i386, Scan, Classify_reloc>( symtab, layout, this, @@ -2507,8 +2646,7 @@ Target_i386::scan_relocs(Symbol_table* symtab, return; } - gold::scan_relocs<32, false, Target_i386, elfcpp::SHT_REL, - Target_i386::Scan>( + gold::scan_relocs<32, false, Target_i386, Scan, Classify_reloc>( symtab, layout, this, @@ -2636,25 +2774,32 @@ Target_i386::Relocate::should_apply_static_reloc(const Sized_symbol<32>* gsym, inline bool Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, - Target_i386* target, - Output_section* output_section, - size_t relnum, - const elfcpp::Rel<32, false>& rel, - unsigned int r_type, - const Sized_symbol<32>* gsym, - const Symbol_value<32>* psymval, - unsigned char* view, - elfcpp::Elf_types<32>::Elf_Addr address, - section_size_type view_size) + unsigned int, + Target_i386* target, + Output_section* output_section, + size_t relnum, + const unsigned char* preloc, + const Sized_symbol<32>* gsym, + const Symbol_value<32>* psymval, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr address, + section_size_type view_size) { + const elfcpp::Rel<32, false> rel(preloc); + unsigned int r_type = elfcpp::elf_r_type<32>(rel.get_r_info()); + if (this->skip_call_tls_get_addr_) { if ((r_type != elfcpp::R_386_PLT32 + && r_type != elfcpp::R_386_GOT32X && r_type != elfcpp::R_386_PC32) || gsym == NULL || strcmp(gsym->name(), "___tls_get_addr") != 0) - gold_error_at_location(relinfo, relnum, rel.get_r_offset(), - _("missing expected TLS relocation")); + { + gold_error_at_location(relinfo, relnum, rel.get_r_offset(), + _("missing expected TLS relocation")); + this->skip_call_tls_get_addr_ = false; + } else { this->skip_call_tls_get_addr_ = false; @@ -2697,34 +2842,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, } } - // Get the GOT offset if needed. - // The GOT pointer points to the end of the GOT section. - // We need to subtract the size of the GOT section to get - // the actual offset to use in the relocation. - bool have_got_offset = false; - unsigned int got_offset = 0; - switch (r_type) - { - case elfcpp::R_386_GOT32: - if (gsym != NULL) - { - gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD)); - got_offset = (gsym->got_offset(GOT_TYPE_STANDARD) - - target->got_size()); - } - else - { - unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info()); - gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD)); - got_offset = (object->local_got_offset(r_sym, GOT_TYPE_STANDARD) - - target->got_size()); - } - have_got_offset = true; - break; - - default: - break; - } + bool baseless; switch (r_type) { @@ -2774,8 +2892,67 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, break; case elfcpp::R_386_GOT32: - gold_assert(have_got_offset); - Relocate_functions<32, false>::rel32(view, got_offset); + case elfcpp::R_386_GOT32X: + baseless = (view[-1] & 0xc7) == 0x5; + // R_386_GOT32 and R_386_GOT32X don't work without base register + // when generating a position-independent output file. + if (baseless + && parameters->options().output_is_position_independent()) + { + if(gsym) + gold_error_at_location(relinfo, relnum, rel.get_r_offset(), + _("unexpected reloc %u against global symbol %s without base register in object file when generating a position-independent output file"), + r_type, gsym->demangled_name().c_str()); + else + gold_error_at_location(relinfo, relnum, rel.get_r_offset(), + _("unexpected reloc %u against local symbol without base register in object file when generating a position-independent output file"), + r_type); + } + + // Convert + // mov foo@GOT(%reg), %reg + // to + // lea foo@GOTOFF(%reg), %reg + // if possible. + if (rel.get_r_offset() >= 2 + && view[-2] == 0x8b + && ((gsym == NULL && !psymval->is_ifunc_symbol()) + || (gsym != NULL + && Target_i386::can_convert_mov_to_lea(gsym)))) + { + view[-2] = 0x8d; + elfcpp::Elf_types<32>::Elf_Addr value; + value = psymval->value(object, 0); + // Don't subtract the .got.plt section address for baseless + // addressing. + if (!baseless) + value -= target->got_plt_section()->address(); + Relocate_functions<32, false>::rel32(view, value); + } + else + { + // The GOT pointer points to the end of the GOT section. + // We need to subtract the size of the GOT section to get + // the actual offset to use in the relocation. + unsigned int got_offset = 0; + if (gsym != NULL) + { + gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD)); + got_offset = (gsym->got_offset(GOT_TYPE_STANDARD) + - target->got_size()); + } + else + { + unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info()); + gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD)); + got_offset = (object->local_got_offset(r_sym, GOT_TYPE_STANDARD) + - target->got_size()); + } + // Add the .got.plt section address for baseless addressing. + if (baseless) + got_offset += target->got_plt_section()->address(); + Relocate_functions<32, false>::rel32(view, got_offset); + } break; case elfcpp::R_386_GOTOFF: @@ -2909,7 +3086,7 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, } if (optimized_type == tls::TLSOPT_TO_IE) { - this->tls_gd_to_ie(relinfo, relnum, tls_segment, rel, r_type, + this->tls_gd_to_ie(relinfo, relnum, rel, r_type, got_offset, view, view_size); break; } @@ -2971,13 +3148,7 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, } if (optimized_type == tls::TLSOPT_TO_IE) { - if (tls_segment == NULL) - { - gold_assert(parameters->errors()->error_count() > 0 - || issue_undefined_symbol_error(gsym)); - return; - } - this->tls_desc_gd_to_ie(relinfo, relnum, tls_segment, rel, r_type, + this->tls_desc_gd_to_ie(relinfo, relnum, rel, r_type, got_offset, view, view_size); break; } @@ -3152,9 +3323,11 @@ Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo, unsigned char* view, section_size_type view_size) { - // leal foo(,%reg,1),%eax; call ___tls_get_addr + // leal foo(,%ebx,1),%eax; call ___tls_get_addr@PLT // ==> movl %gs:0,%eax; subl $foo@tpoff,%eax - // leal foo(%reg),%eax; call ___tls_get_addr + // leal foo(%ebx),%eax; call ___tls_get_addr@PLT + // ==> movl %gs:0,%eax; subl $foo@tpoff,%eax + // leal foo(%reg),%eax; call *___tls_get_addr@GOT(%reg) // ==> movl %gs:0,%eax; subl $foo@tpoff,%eax tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2); @@ -3162,10 +3335,12 @@ Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo, unsigned char op1 = view[-1]; unsigned char op2 = view[-2]; + unsigned char op3 = view[4]; tls::check_tls(relinfo, relnum, rel.get_r_offset(), op2 == 0x8d || op2 == 0x04); - tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[4] == 0xe8); + tls::check_tls(relinfo, relnum, rel.get_r_offset(), + op3 == 0xe8 || op3 == 0xff); int roff = 5; @@ -3179,12 +3354,18 @@ Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo, } else { + unsigned char reg = op1 & 7; tls::check_tls(relinfo, relnum, rel.get_r_offset(), - (op1 & 0xf8) == 0x80 && (op1 & 7) != 4); - if (rel.get_r_offset() + 9 < view_size - && view[9] == 0x90) + ((op1 & 0xf8) == 0x80 + && reg != 4 + && reg != 0 + && (op3 == 0xe8 || (view[5] & 0x7) == reg))); + if (op3 == 0xff + || (rel.get_r_offset() + 9 < view_size + && view[9] == 0x90)) { - // There is a trailing nop. Use the size byte subl. + // There is an indirect call or a trailing nop. Use the size + // byte subl. memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); roff = 6; } @@ -3209,30 +3390,32 @@ Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo, inline void Target_i386::Relocate::tls_gd_to_ie(const Relocate_info<32, false>* relinfo, size_t relnum, - Output_segment*, const elfcpp::Rel<32, false>& rel, unsigned int, elfcpp::Elf_types<32>::Elf_Addr value, unsigned char* view, section_size_type view_size) { - // leal foo(,%ebx,1),%eax; call ___tls_get_addr + // leal foo(,%ebx,1),%eax; call ___tls_get_addr@PLT + // ==> movl %gs:0,%eax; addl foo@gotntpoff(%ebx),%eax + // leal foo(%ebx),%eax; call ___tls_get_addr@PLT; nop // ==> movl %gs:0,%eax; addl foo@gotntpoff(%ebx),%eax + // leal foo(%reg),%eax; call *___tls_get_addr@GOT(%reg) + // ==> movl %gs:0,%eax; addl foo@gotntpoff(%reg),%eax tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2); tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 9); unsigned char op1 = view[-1]; unsigned char op2 = view[-2]; + unsigned char op3 = view[4]; tls::check_tls(relinfo, relnum, rel.get_r_offset(), op2 == 0x8d || op2 == 0x04); - tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[4] == 0xe8); - - int roff = 5; + tls::check_tls(relinfo, relnum, rel.get_r_offset(), + op3 == 0xe8 || op3 == 0xff); - // FIXME: For now, support only the first (SIB) form. - tls::check_tls(relinfo, relnum, rel.get_r_offset(), op2 == 0x04); + int roff; if (op2 == 0x04) { @@ -3240,28 +3423,22 @@ Target_i386::Relocate::tls_gd_to_ie(const Relocate_info<32, false>* relinfo, tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[-3] == 0x8d); tls::check_tls(relinfo, relnum, rel.get_r_offset(), ((op1 & 0xc7) == 0x05 && op1 != (4 << 3))); - memcpy(view - 3, "\x65\xa1\0\0\0\0\x03\x83\0\0\0", 12); + roff = 5; } else { + unsigned char reg = op1 & 7; + tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 10); tls::check_tls(relinfo, relnum, rel.get_r_offset(), - (op1 & 0xf8) == 0x80 && (op1 & 7) != 4); - if (rel.get_r_offset() + 9 < view_size - && view[9] == 0x90) - { - // FIXME: This is not the right instruction sequence. - // There is a trailing nop. Use the size byte subl. - memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); - roff = 6; - } - else - { - // FIXME: This is not the right instruction sequence. - // Use the five byte subl. - memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11); - } + ((op1 & 0xf8) == 0x80 + && reg != 4 + && reg != 0 + && ((op3 == 0xe8 && view[9] == 0x90) + || (view[5] & 0x7) == reg))); + roff = 6; } + memcpy(view + roff - 8, "\x65\xa1\0\0\0\0\x03\x83\0\0\0", 12); Relocate_functions<32, false>::rel32(view + roff, value); // The next reloc should be a PLT32 reloc against __tls_get_addr. @@ -3315,7 +3492,6 @@ inline void Target_i386::Relocate::tls_desc_gd_to_ie( const Relocate_info<32, false>* relinfo, size_t relnum, - Output_segment*, const elfcpp::Rel<32, false>& rel, unsigned int r_type, elfcpp::Elf_types<32>::Elf_Addr value, @@ -3359,19 +3535,36 @@ Target_i386::Relocate::tls_ld_to_le(const Relocate_info<32, false>* relinfo, unsigned char* view, section_size_type view_size) { - // leal foo(%reg), %eax; call ___tls_get_addr + // leal foo(%ebx), %eax; call ___tls_get_addr@PLT // ==> movl %gs:0,%eax; nop; leal 0(%esi,1),%esi + // leal foo(%reg), %eax; call call *___tls_get_addr@GOT(%reg) + // ==> movl %gs:0,%eax; leal (%esi),%esi tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2); - tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 9); - // FIXME: Does this test really always pass? + unsigned char op1 = view[-1]; + unsigned char op2 = view[-2]; + unsigned char op3 = view[4]; + tls::check_tls(relinfo, relnum, rel.get_r_offset(), - view[-2] == 0x8d && view[-1] == 0x83); + op3 == 0xe8 || op3 == 0xff); + tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, + op3 == 0xe8 ? 9 : 10); - tls::check_tls(relinfo, relnum, rel.get_r_offset(), view[4] == 0xe8); + // FIXME: Does this test really always pass? + tls::check_tls(relinfo, relnum, rel.get_r_offset(), op2 == 0x8d); - memcpy(view - 2, "\x65\xa1\0\0\0\0\x90\x8d\x74\x26\0", 11); + unsigned char reg = op1 & 7; + tls::check_tls(relinfo, relnum, rel.get_r_offset(), + ((op1 & 0xf8) == 0x80 + && reg != 4 + && reg != 0 + && (op3 == 0xe8 || (view[5] & 0x7) == reg))); + + if (op3 == 0xe8) + memcpy(view - 2, "\x65\xa1\0\0\0\0\x90\x8d\x74\x26\0", 11); + else + memcpy(view - 2, "\x65\xa1\0\0\0\0\x8d\xb6\0\0\0\0", 12); // The next reloc should be a PLT32 reloc against __tls_get_addr. // We can skip it. @@ -3490,8 +3683,8 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, { gold_assert(sh_type == elfcpp::SHT_REL); - gold::relocate_section<32, false, Target_i386, elfcpp::SHT_REL, - Target_i386::Relocate, gold::Default_comdat_behavior>( + gold::relocate_section<32, false, Target_i386, Relocate, + gold::Default_comdat_behavior, Classify_reloc>( relinfo, this, prelocs, @@ -3508,7 +3701,7 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, // link. unsigned int -Target_i386::Relocatable_size_for_reloc::get_size_for_reloc( +Target_i386::Classify_reloc::get_size_for_reloc( unsigned int r_type, Relobj* object) { @@ -3532,6 +3725,7 @@ Target_i386::Relocatable_size_for_reloc::get_size_for_reloc( case elfcpp::R_386_32: case elfcpp::R_386_PC32: case elfcpp::R_386_GOT32: + case elfcpp::R_386_GOT32X: case elfcpp::R_386_PLT32: case elfcpp::R_386_GOTOFF: case elfcpp::R_386_GOTPC: @@ -3592,13 +3786,12 @@ Target_i386::scan_relocatable_relocs(Symbol_table* symtab, const unsigned char* plocal_symbols, Relocatable_relocs* rr) { - gold_assert(sh_type == elfcpp::SHT_REL); + typedef gold::Default_scan_relocatable_relocs + Scan_relocatable_relocs; - typedef gold::Default_scan_relocatable_relocs Scan_relocatable_relocs; + gold_assert(sh_type == elfcpp::SHT_REL); - gold::scan_relocatable_relocs<32, false, elfcpp::SHT_REL, - Scan_relocatable_relocs>( + gold::scan_relocatable_relocs<32, false, Scan_relocatable_relocs>( symtab, layout, object, @@ -3612,6 +3805,43 @@ Target_i386::scan_relocatable_relocs(Symbol_table* symtab, rr); } +// Scan the relocs for --emit-relocs. + +void +Target_i386::emit_relocs_scan(Symbol_table* symtab, + Layout* layout, + Sized_relobj_file<32, false>* object, + unsigned int data_shndx, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, + size_t local_symbol_count, + const unsigned char* plocal_syms, + Relocatable_relocs* rr) +{ + typedef gold::Default_classify_reloc + Classify_reloc; + typedef gold::Default_emit_relocs_strategy + Emit_relocs_strategy; + + gold_assert(sh_type == elfcpp::SHT_REL); + + gold::scan_relocatable_relocs<32, false, Emit_relocs_strategy>( + symtab, + layout, + object, + data_shndx, + prelocs, + reloc_count, + output_section, + needs_special_offset_handling, + local_symbol_count, + plocal_syms, + rr); +} + // Emit relocations for a section. void @@ -3622,7 +3852,6 @@ Target_i386::relocate_relocs( size_t reloc_count, Output_section* output_section, elfcpp::Elf_types<32>::Elf_Off offset_in_output_section, - const Relocatable_relocs* rr, unsigned char* view, elfcpp::Elf_types<32>::Elf_Addr view_address, section_size_type view_size, @@ -3631,13 +3860,12 @@ Target_i386::relocate_relocs( { gold_assert(sh_type == elfcpp::SHT_REL); - gold::relocate_relocs<32, false, elfcpp::SHT_REL>( + gold::relocate_relocs<32, false, Classify_reloc>( relinfo, prelocs, reloc_count, output_section, offset_in_output_section, - rr, view, view_address, view_size, @@ -3749,7 +3977,10 @@ Target_i386::do_ehframe_datarel_base() const // get_pc_thunk function. bool -Target_i386::do_is_call_to_non_split(const Symbol* sym, unsigned int) const +Target_i386::do_is_call_to_non_split(const Symbol* sym, + const unsigned char*, + const unsigned char*, + section_size_type) const { return (sym->type() == elfcpp::STT_FUNC && !is_prefix_of("__i686.get_pc_thunk.", sym->name())); @@ -3764,6 +3995,8 @@ void Target_i386::do_calls_non_split(Relobj* object, unsigned int shndx, section_offset_type fnoffset, section_size_type fnsize, + const unsigned char*, + size_t, unsigned char* view, section_size_type view_size, std::string* from, @@ -3837,7 +4070,7 @@ class Output_data_plt_i386_nacl : public Output_data_plt_i386 { public: Output_data_plt_i386_nacl(Layout* layout, - Output_data_space* got_plt, + Output_data_got_plt_i386* got_plt, Output_data_space* got_irelative) : Output_data_plt_i386(layout, plt_entry_size, got_plt, got_irelative) { } @@ -3866,7 +4099,7 @@ class Output_data_plt_i386_nacl_exec : public Output_data_plt_i386_nacl { public: Output_data_plt_i386_nacl_exec(Layout* layout, - Output_data_space* got_plt, + Output_data_got_plt_i386* got_plt, Output_data_space* got_irelative) : Output_data_plt_i386_nacl(layout, got_plt, got_irelative) { } @@ -3895,7 +4128,7 @@ class Output_data_plt_i386_nacl_dyn : public Output_data_plt_i386_nacl { public: Output_data_plt_i386_nacl_dyn(Layout* layout, - Output_data_space* got_plt, + Output_data_got_plt_i386* got_plt, Output_data_space* got_irelative) : Output_data_plt_i386_nacl(layout, got_plt, got_irelative) { } @@ -3929,7 +4162,7 @@ class Target_i386_nacl : public Target_i386 protected: virtual Output_data_plt_i386* do_make_data_plt(Layout* layout, - Output_data_space* got_plt, + Output_data_got_plt_i386* got_plt, Output_data_space* got_irelative, bool dyn) { @@ -3969,7 +4202,9 @@ const Target::Target_info Target_i386_nacl::i386_nacl_info = 0, // large_common_section_flags NULL, // attributes_section NULL, // attributes_vendor - "_start" // entry_symbol_name + "_start", // entry_symbol_name + 32, // hash_entry_size + elfcpp::SHT_PROGBITS, // unwind_section_type }; #define NACLMASK 0xe0 // 32-byte alignment mask @@ -4167,4 +4402,62 @@ class Target_selector_i386_nacl Target_selector_i386_nacl target_selector_i386; +// IAMCU variant. It uses EM_IAMCU, not EM_386. + +class Target_iamcu : public Target_i386 +{ + public: + Target_iamcu() + : Target_i386(&iamcu_info) + { } + + private: + // Information about this specific target which we pass to the + // general Target structure. + static const Target::Target_info iamcu_info; +}; + +const Target::Target_info Target_iamcu::iamcu_info = +{ + 32, // size + false, // is_big_endian + elfcpp::EM_IAMCU, // machine_code + false, // has_make_symbol + false, // has_resolve + true, // has_code_fill + true, // is_default_stack_executable + true, // can_icf_inline_merge_sections + '\0', // wrap_char + "/usr/lib/libc.so.1", // dynamic_linker + 0x08048000, // default_text_segment_address + 0x1000, // abi_pagesize (overridable by -z max-page-size) + 0x1000, // common_pagesize (overridable by -z common-page-size) + false, // isolate_execinstr + 0, // rosegment_gap + elfcpp::SHN_UNDEF, // small_common_shndx + elfcpp::SHN_UNDEF, // large_common_shndx + 0, // small_common_section_flags + 0, // large_common_section_flags + NULL, // attributes_section + NULL, // attributes_vendor + "_start", // entry_symbol_name + 32, // hash_entry_size + elfcpp::SHT_PROGBITS, // unwind_section_type +}; + +class Target_selector_iamcu : public Target_selector +{ +public: + Target_selector_iamcu() + : Target_selector(elfcpp::EM_IAMCU, 32, false, "elf32-iamcu", + "elf_iamcu") + { } + + Target* + do_instantiate_target() + { return new Target_iamcu(); } +}; + +Target_selector_iamcu target_selector_iamcu; + } // End anonymous namespace.