X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gold%2Fi386.cc;h=3a8735f3a7164569731ca697955204c5301f0f6e;hb=7c392d1de1400202eb86f7679628c4b7c14f8108;hp=afb9c31f0aef8cebec9a159c6807f2c9243b750a;hpb=28a13fec147af8768d92f44d5d6e7ac10b918e82;p=deliverable%2Fbinutils-gdb.git diff --git a/gold/i386.cc b/gold/i386.cc index afb9c31f0a..3a8735f3a7 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -1,6 +1,6 @@ // i386.cc -- i386 target support for gold. -// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. +// Copyright (C) 2006-2020 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. @@ -39,6 +39,7 @@ #include "target-select.h" #include "tls.h" #include "freebsd.h" +#include "nacl.h" #include "gc.h" namespace @@ -46,22 +47,53 @@ 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 +// classes below fill in those details. class Output_data_plt_i386 : public Output_section_data { public: typedef Output_data_reloc Reloc_section; - Output_data_plt_i386(Symbol_table*, Layout*, Output_data_space*); + Output_data_plt_i386(Layout*, uint64_t addralign, + Output_data_got_plt_i386*, Output_data_space*); // Add an entry to the PLT. void - add_entry(Symbol* gsym); + add_entry(Symbol_table*, Layout*, Symbol* gsym); // Add an entry to the PLT for a local STT_GNU_IFUNC symbol. unsigned int - add_local_ifunc_entry(Sized_relobj_file<32, false>* relobj, + add_local_ifunc_entry(Symbol_table*, Layout*, + Sized_relobj_file<32, false>* relobj, unsigned int local_sym_index); // Return the .rel.plt section data. @@ -73,56 +105,106 @@ class Output_data_plt_i386 : public Output_section_data Reloc_section* rel_tls_desc(Layout*); + // Return where the IRELATIVE relocations should go. + Reloc_section* + rel_irelative(Symbol_table*, Layout*); + + // Return whether we created a section for IRELATIVE relocations. + bool + has_irelative_section() const + { return this->irelative_rel_ != NULL; } + // Return the number of PLT entries. unsigned int entry_count() const - { return this->count_; } + { return this->count_ + this->irelative_count_; } // Return the offset of the first non-reserved PLT entry. - static unsigned int + unsigned int first_plt_entry_offset() - { return plt_entry_size; } + { return this->get_plt_entry_size(); } // Return the size of a PLT entry. - static unsigned int - get_plt_entry_size() - { return plt_entry_size; } + unsigned int + get_plt_entry_size() const + { return this->do_get_plt_entry_size(); } - protected: + // Return the PLT address to use for a global symbol. + uint64_t + address_for_global(const Symbol*); + + // Return the PLT address to use for a local symbol. + uint64_t + address_for_local(const Relobj*, unsigned int symndx); + + // Add .eh_frame information for the PLT. void - do_adjust_output_section(Output_section* os); + add_eh_frame(Layout* layout) + { this->do_add_eh_frame(layout); } - // Write to a map file. + protected: + // Fill the first PLT entry, given the pointer to the PLT section data + // and the runtime address of the GOT. void - do_print_to_mapfile(Mapfile* mapfile) const - { mapfile->print_output_data(this, _("** PLT")); } + fill_first_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address) + { this->do_fill_first_plt_entry(pov, got_address); } + + // Fill a normal PLT entry, given the pointer to the entry's data in the + // section, the runtime address of the GOT, the offset into the GOT of + // the corresponding slot, the offset into the relocation section of the + // corresponding reloc, and the offset of this entry within the whole + // PLT. Return the offset from this PLT entry's runtime address that + // should be used to compute the initial value of the GOT slot. + unsigned int + fill_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset) + { + return this->do_fill_plt_entry(pov, got_address, got_offset, + plt_offset, plt_rel_offset); + } - private: - // The size of an entry in the PLT. - static const int plt_entry_size = 16; + virtual unsigned int + do_get_plt_entry_size() const = 0; - // The first entry in the PLT for an executable. - static const unsigned char exec_first_plt_entry[plt_entry_size]; + virtual void + do_fill_first_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address) = 0; - // The first entry in the PLT for a shared object. - static const unsigned char dyn_first_plt_entry[plt_entry_size]; + virtual unsigned int + do_fill_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset) = 0; - // Other entries in the PLT for an executable. - static const unsigned char exec_plt_entry[plt_entry_size]; + virtual void + do_add_eh_frame(Layout*) = 0; - // Other entries in the PLT for a shared object. - static const unsigned char dyn_plt_entry[plt_entry_size]; + 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")); } // The .eh_frame unwind information for the PLT. + // The CIE is common across variants of the PLT format. static const int plt_eh_frame_cie_size = 16; - static const int plt_eh_frame_fde_size = 32; static const unsigned char plt_eh_frame_cie[plt_eh_frame_cie_size]; - static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size]; + private: // Set the final size. void set_final_data_size() - { this->set_data_size((this->count_ + 1) * plt_entry_size); } + { + this->set_data_size((this->count_ + this->irelative_count_ + 1) + * this->get_plt_entry_size()); + } // Write out the PLT data. void @@ -150,16 +232,119 @@ class Output_data_plt_i386 : public Output_section_data // The TLS_DESC relocations, if necessary. These must follow the // regular PLT relocs. Reloc_section* tls_desc_rel_; + // The IRELATIVE relocations, if necessary. These must follow the + // 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. unsigned int count_; + // Number of PLT entries with R_386_IRELATIVE relocs. These follow + // the regular PLT entries. + unsigned int irelative_count_; // Global STT_GNU_IFUNC symbols. std::vector global_ifuncs_; // Local STT_GNU_IFUNC symbols. std::vector local_ifuncs_; }; +// This is an abstract class for the standard PLT layout. +// The derived classes below handle the actual PLT contents +// for the executable (non-PIC) and shared-library (PIC) cases. +// The unwind information is uniform across those two, so it's here. + +class Output_data_plt_i386_standard : public Output_data_plt_i386 +{ + public: + Output_data_plt_i386_standard(Layout* layout, + Output_data_got_plt_i386* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_i386(layout, plt_entry_size, got_plt, got_irelative) + { } + + protected: + virtual unsigned int + do_get_plt_entry_size() const + { return plt_entry_size; } + + virtual void + do_add_eh_frame(Layout* layout) + { + layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size, + plt_eh_frame_fde, plt_eh_frame_fde_size); + } + + // The size of an entry in the PLT. + static const int plt_entry_size = 16; + + // The .eh_frame unwind information for the PLT. + static const int plt_eh_frame_fde_size = 32; + static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size]; +}; + +// Actually fill the PLT contents for an executable (non-PIC). + +class Output_data_plt_i386_exec : public Output_data_plt_i386_standard +{ +public: + Output_data_plt_i386_exec(Layout* layout, + Output_data_got_plt_i386* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_i386_standard(layout, got_plt, got_irelative) + { } + + protected: + virtual void + do_fill_first_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address); + + virtual unsigned int + do_fill_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset); + + private: + // The first entry in the PLT for an executable. + static const unsigned char first_plt_entry[plt_entry_size]; + + // Other entries in the PLT for an executable. + static const unsigned char plt_entry[plt_entry_size]; +}; + +// Actually fill the PLT contents for a shared library (PIC). + +class Output_data_plt_i386_dyn : public Output_data_plt_i386_standard +{ + public: + Output_data_plt_i386_dyn(Layout* layout, + Output_data_got_plt_i386* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_i386_standard(layout, got_plt, got_irelative) + { } + + protected: + virtual void + do_fill_first_plt_entry(unsigned char* pov, elfcpp::Elf_types<32>::Elf_Addr); + + virtual unsigned int + do_fill_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset); + + private: + // The first entry in the PLT for a shared object. + static const unsigned char first_plt_entry[plt_entry_size]; + + // Other entries in the PLT for a shared object. + static const unsigned char plt_entry[plt_entry_size]; +}; + // The i386 target class. // TLS info comes from // http://people.redhat.com/drepper/tls.pdf @@ -170,28 +355,28 @@ class Target_i386 : public Sized_target<32, false> public: typedef Output_data_reloc Reloc_section; - Target_i386() - : Sized_target<32, false>(&i386_info), - got_(NULL), plt_(NULL), got_plt_(NULL), got_tlsdesc_(NULL), - global_offset_table_(NULL), rel_dyn_(NULL), - copy_relocs_(elfcpp::R_386_COPY), dynbss_(NULL), + Target_i386(const Target::Target_info* info = &i386_info) + : 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), got_mod_index_offset_(-1U), tls_base_symbol_defined_(false) { } - // Process the relocations to determine unreferenced sections for + // Process the relocations to determine unreferenced sections for // garbage collection. void gc_process_relocs(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_symbols); + 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_symbols); // Scan the relocations to look for symbol adjustments. void @@ -244,20 +429,34 @@ class Target_i386 : public Sized_target<32, false> const unsigned char* plocal_symbols, Relocatable_relocs*); - // Relocate a section during a relocatable link. + // 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_for_relocatable(const Relocate_info<32, false>*, - unsigned int sh_type, - const unsigned char* prelocs, - size_t reloc_count, - Output_section* output_section, - off_t offset_in_output_section, - const Relocatable_relocs*, - unsigned char* view, - elfcpp::Elf_types<32>::Elf_Addr view_address, - section_size_type view_size, - unsigned char* reloc_view, - section_size_type reloc_view_size); + relocate_relocs(const Relocate_info<32, false>*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + Output_section* output_section, + elfcpp::Elf_types<32>::Elf_Off offset_in_output_section, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr view_address, + section_size_type view_size, + unsigned char* reloc_view, + section_size_type reloc_view_size); // Return a string used to fill a code section with nops. std::string @@ -281,14 +480,14 @@ class Target_i386 : public Sized_target<32, false> return Target::do_is_local_label_name(name); } - // Return the PLT section. - Output_data* - do_plt_section_for_global(const Symbol*) const - { return this->plt_section(); } + // Return the PLT address to use for a global symbol. + uint64_t + do_plt_address_for_global(const Symbol* gsym) const + { return this->plt_section()->address_for_global(gsym); } - Output_data* - do_plt_section_for_local(const Relobj*, unsigned int) const - { return this->plt_section(); } + uint64_t + do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const + { return this->plt_section()->address_for_local(relobj, symndx); } // We can tell whether we take the address of a function. inline bool @@ -301,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; @@ -339,6 +540,28 @@ class Target_i386 : public Sized_target<32, false> unsigned int plt_entry_size() const; + protected: + // Instantiate the plt_ member. + // This chooses the right PLT flavor for an executable or a shared object. + Output_data_plt_i386* + make_data_plt(Layout* layout, + 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_got_plt_i386* got_plt, + Output_data_space* got_irelative, + bool dyn) + { + if (dyn) + return new Output_data_plt_i386_dyn(layout, got_plt, got_irelative); + else + return new Output_data_plt_i386_exec(layout, got_plt, got_irelative); + } + private: // The class which scans relocations. struct Scan @@ -353,7 +576,8 @@ class Target_i386 : public Sized_target<32, false> unsigned int data_shndx, Output_section* output_section, const elfcpp::Rel<32, false>& reloc, unsigned int r_type, - const elfcpp::Sym<32, false>& lsym); + const elfcpp::Sym<32, false>& lsym, + bool is_discarded); inline void global(Symbol_table* symtab, Layout* layout, Target_i386* target, @@ -365,23 +589,23 @@ class Target_i386 : public Sized_target<32, false> inline bool local_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout, - Target_i386* target, - Sized_relobj_file<32, false>* object, - unsigned int data_shndx, - Output_section* output_section, - const elfcpp::Rel<32, false>& reloc, + Target_i386* target, + Sized_relobj_file<32, false>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rel<32, false>& reloc, unsigned int r_type, - const elfcpp::Sym<32, false>& lsym); + const elfcpp::Sym<32, false>& lsym); inline bool global_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout, Target_i386* target, - Sized_relobj_file<32, false>* object, - unsigned int data_shndx, - Output_section* output_section, + Sized_relobj_file<32, false>* object, + unsigned int data_shndx, + Output_section* output_section, const elfcpp::Rel<32, false>& reloc, unsigned int r_type, - Symbol* gsym); + Symbol* gsym); inline bool possible_function_pointer_reloc(unsigned int r_type); @@ -419,17 +643,16 @@ class Target_i386 : public Sized_target<32, false> // Return whether the static relocation needs to be applied. inline bool should_apply_static_reloc(const Sized_symbol<32>* gsym, - unsigned int r_type, - bool is_32bit, + unsigned int r_type, + 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 - 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); @@ -437,7 +660,7 @@ class Target_i386 : public Sized_target<32, false> // Do a TLS relocation. inline void relocate_tls(const Relocate_info<32, false>*, Target_i386* target, - size_t relnum, const elfcpp::Rel<32, false>&, + size_t relnum, const elfcpp::Rel<32, false>&, unsigned int r_type, const Sized_symbol<32>*, const Symbol_value<32>*, unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, @@ -446,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, @@ -465,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, @@ -516,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*); }; @@ -530,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); @@ -589,16 +840,22 @@ class Target_i386 : public Sized_target<32, false> Reloc_section* rel_tls_desc_section(Layout*) const; + // Get the section to use for IRELATIVE relocations. + Reloc_section* + rel_irelative_section(Layout*); + // Add a potential copy relocation. void copy_reloc(Symbol_table* symtab, Layout* layout, - Sized_relobj_file<32, false>* object, + Sized_relobj_file<32, false>* object, 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)); } @@ -624,17 +881,19 @@ 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. Output_data_got<32, false>* got_tlsdesc_; // The _GLOBAL_OFFSET_TABLE_ symbol. Symbol* global_offset_table_; // The dynamic reloc section. Reloc_section* rel_dyn_; + // The section to use for IRELATIVE relocs. + 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. @@ -656,12 +915,17 @@ const Target::Target_info Target_i386::i386_info = 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 + NULL, // attributes_vendor + "_start", // entry_symbol_name + 32, // hash_entry_size + elfcpp::SHT_PROGBITS, // unwind_section_type }; // Get the GOT section, creating it if necessary. @@ -675,23 +939,37 @@ Target_i386::got_section(Symbol_table* symtab, Layout* layout) this->got_ = new Output_data_got<32, false>(); + // When using -z now, we can treat .got.plt as a relro section. + // Without -z now, it is modified after program startup by lazy + // PLT relocations. + bool is_got_plt_relro = parameters->options().now(); + Output_section_order got_order = (is_got_plt_relro + ? ORDER_RELRO + : ORDER_RELRO_LAST); + Output_section_order got_plt_order = (is_got_plt_relro + ? ORDER_RELRO + : ORDER_NON_RELRO_FIRST); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), - this->got_, ORDER_RELRO_LAST, true); + 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), - this->got_plt_, ORDER_NON_RELRO_FIRST, - false); + this->got_plt_, got_plt_order, + is_got_plt_relro); // The first three entries are reserved. this->got_plt_->set_current_data_size(3 * 4); - // Those bytes can go into the relro segment. - layout->increase_relro(3 * 4); + if (!is_got_plt_relro) + { + // Those bytes can go into the relro segment. + layout->increase_relro(3 * 4); + } // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT. this->global_offset_table_ = @@ -703,6 +981,15 @@ Target_i386::got_section(Symbol_table* symtab, Layout* layout) elfcpp::STV_HIDDEN, 0, false, false); + // If there are any IRELATIVE relocations, they get GOT entries + // in .got.plt after the jump slot relocations. + this->got_irelative_ = new Output_data_space(4, "** GOT IRELATIVE PLT"); + layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, + (elfcpp::SHF_ALLOC + | elfcpp::SHF_WRITE), + this->got_irelative_, + got_plt_order, is_got_plt_relro); + // If there are any TLSDESC relocations, they get GOT entries in // .got.plt after the jump slot entries. this->got_tlsdesc_ = new Output_data_got<32, false>(); @@ -710,7 +997,7 @@ Target_i386::got_section(Symbol_table* symtab, Layout* layout) (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), this->got_tlsdesc_, - ORDER_NON_RELRO_FIRST, false); + got_plt_order, is_got_plt_relro); } return this->got_; @@ -732,43 +1019,67 @@ Target_i386::rel_dyn_section(Layout* layout) return this->rel_dyn_; } +// Get the section to use for IRELATIVE relocs, creating it if +// necessary. These go in .rel.dyn, but only after all other dynamic +// relocations. They need to follow the other dynamic relocations so +// that they can refer to global variables initialized by those +// relocs. + +Target_i386::Reloc_section* +Target_i386::rel_irelative_section(Layout* layout) +{ + if (this->rel_irelative_ == NULL) + { + // Make sure we have already create the dynamic reloc section. + this->rel_dyn_section(layout); + this->rel_irelative_ = new Reloc_section(false); + layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL, + elfcpp::SHF_ALLOC, this->rel_irelative_, + ORDER_DYNAMIC_RELOCS, false); + gold_assert(this->rel_dyn_->output_section() + == this->rel_irelative_->output_section()); + } + 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(Symbol_table* symtab, - Layout* layout, - Output_data_space* got_plt) - : Output_section_data(16), tls_desc_rel_(NULL), got_plt_(got_plt), count_(0), +Output_data_plt_i386::Output_data_plt_i386(Layout* layout, + uint64_t addralign, + Output_data_got_plt_i386* got_plt, + Output_data_space* got_irelative) + : Output_section_data(addralign), + 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, elfcpp::SHF_ALLOC, this->rel_, ORDER_DYNAMIC_PLT_RELOCS, false); - - if (parameters->doing_static_link()) - { - // A statically linked executable will only have a .rel.plt - // section to hold R_386_IRELATIVE relocs for STT_GNU_IFUNC - // symbols. The library will use these symbols to locate the - // IRELATIVE relocs at program startup time. - symtab->define_in_output_data("__rel_iplt_start", NULL, - Symbol_table::PREDEFINED, - this->rel_, 0, 0, elfcpp::STT_NOTYPE, - elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, - 0, false, true); - symtab->define_in_output_data("__rel_iplt_end", NULL, - Symbol_table::PREDEFINED, - this->rel_, 0, 0, elfcpp::STT_NOTYPE, - elfcpp::STB_GLOBAL, elfcpp::STV_HIDDEN, - 0, true, true); - } - - // Add unwind information if requested. - if (parameters->options().ld_generated_unwind_info()) - layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size, - plt_eh_frame_fde, plt_eh_frame_fde_size); } void @@ -782,29 +1093,23 @@ Output_data_plt_i386::do_adjust_output_section(Output_section* os) // Add an entry to the PLT. void -Output_data_plt_i386::add_entry(Symbol* gsym) +Output_data_plt_i386::add_entry(Symbol_table* symtab, Layout* layout, + 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_ + 1) * plt_entry_size); - - ++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. if (gsym->type() == elfcpp::STT_GNU_IFUNC && gsym->can_use_relative_reloc(false)) { - this->rel_->add_symbolless_global_addend(gsym, elfcpp::R_386_IRELATIVE, - this->got_plt_, got_offset); + gsym->set_plt_offset(this->irelative_count_ * this->get_plt_entry_size()); + ++this->irelative_count_; + section_offset_type got_offset = + this->got_irelative_->current_data_size(); + this->got_irelative_->set_current_data_size(got_offset + 4); + Reloc_section* rel = this->rel_irelative(symtab, layout); + rel->add_symbolless_global_addend(gsym, elfcpp::R_386_IRELATIVE, + this->got_irelative_, got_offset); struct Global_ifunc gi; gi.sym = gsym; gi.got_offset = got_offset; @@ -812,6 +1117,19 @@ Output_data_plt_i386::add_entry(Symbol* gsym) } else { + // When setting the PLT offset we skip the initial reserved PLT + // entry. + gsym->set_plt_offset((this->count_ + 1) * this->get_plt_entry_size()); + + ++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); + gsym->set_needs_dynsym_entry(); this->rel_->add_global(gsym, elfcpp::R_386_JUMP_SLOT, this->got_plt_, got_offset); @@ -827,22 +1145,25 @@ Output_data_plt_i386::add_entry(Symbol* gsym) unsigned int Output_data_plt_i386::add_local_ifunc_entry( + Symbol_table* symtab, + Layout* layout, Sized_relobj_file<32, false>* relobj, unsigned int local_sym_index) { - unsigned int plt_offset = (this->count_ + 1) * plt_entry_size; - ++this->count_; + unsigned int plt_offset = this->irelative_count_ * this->get_plt_entry_size(); + ++this->irelative_count_; - section_offset_type got_offset = this->got_plt_->current_data_size(); + section_offset_type got_offset = this->got_irelative_->current_data_size(); // Every PLT entry needs a GOT entry which points back to the PLT // entry. - this->got_plt_->set_current_data_size(got_offset + 4); + this->got_irelative_->set_current_data_size(got_offset + 4); // Every PLT entry needs a reloc. - this->rel_->add_symbolless_local_addend(relobj, local_sym_index, - elfcpp::R_386_IRELATIVE, - this->got_plt_, got_offset); + Reloc_section* rel = this->rel_irelative(symtab, layout); + rel->add_symbolless_local_addend(relobj, local_sym_index, + elfcpp::R_386_IRELATIVE, + this->got_irelative_, got_offset); struct Local_ifunc li; li.object = relobj; @@ -865,15 +1186,78 @@ Output_data_plt_i386::rel_tls_desc(Layout* layout) layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, elfcpp::SHF_ALLOC, this->tls_desc_rel_, ORDER_DYNAMIC_PLT_RELOCS, false); - gold_assert(this->tls_desc_rel_->output_section() == - this->rel_->output_section()); + gold_assert(this->tls_desc_rel_->output_section() + == this->rel_->output_section()); } return this->tls_desc_rel_; } +// Return where the IRELATIVE relocations should go in the PLT. These +// follow the JUMP_SLOT and TLS_DESC relocations. + +Output_data_plt_i386::Reloc_section* +Output_data_plt_i386::rel_irelative(Symbol_table* symtab, Layout* layout) +{ + if (this->irelative_rel_ == NULL) + { + // Make sure we have a place for the TLS_DESC relocations, in + // case we see any later on. + this->rel_tls_desc(layout); + this->irelative_rel_ = new Reloc_section(false); + layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, + elfcpp::SHF_ALLOC, this->irelative_rel_, + ORDER_DYNAMIC_PLT_RELOCS, false); + gold_assert(this->irelative_rel_->output_section() + == this->rel_->output_section()); + + if (parameters->doing_static_link()) + { + // A statically linked executable will only have a .rel.plt + // section to hold R_386_IRELATIVE relocs for STT_GNU_IFUNC + // symbols. The library will use these symbols to locate + // the IRELATIVE relocs at program startup time. + symtab->define_in_output_data("__rel_iplt_start", NULL, + Symbol_table::PREDEFINED, + this->irelative_rel_, 0, 0, + elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL, + elfcpp::STV_HIDDEN, 0, false, true); + symtab->define_in_output_data("__rel_iplt_end", NULL, + Symbol_table::PREDEFINED, + this->irelative_rel_, 0, 0, + elfcpp::STT_NOTYPE, elfcpp::STB_GLOBAL, + elfcpp::STV_HIDDEN, 0, true, true); + } + } + return this->irelative_rel_; +} + +// Return the PLT address to use for a global symbol. + +uint64_t +Output_data_plt_i386::address_for_global(const Symbol* gsym) +{ + uint64_t offset = 0; + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + offset = (this->count_ + 1) * this->get_plt_entry_size(); + return this->address() + offset + gsym->plt_offset(); +} + +// Return the PLT address to use for a local symbol. These are always +// IRELATIVE relocs. + +uint64_t +Output_data_plt_i386::address_for_local(const Relobj* object, + unsigned int r_sym) +{ + return (this->address() + + (this->count_ + 1) * this->get_plt_entry_size() + + object->local_plt_offset(r_sym)); +} + // The first entry in the PLT for an executable. -const unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_i386_exec::first_plt_entry[plt_entry_size] = { 0xff, 0x35, // pushl contents of memory address 0, 0, 0, 0, // replaced with address of .got + 4 @@ -882,18 +1266,36 @@ const unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] = 0, 0, 0, 0 // unused }; +void +Output_data_plt_i386_exec::do_fill_first_plt_entry( + unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address) +{ + memcpy(pov, first_plt_entry, plt_entry_size); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_address + 4); + elfcpp::Swap<32, false>::writeval(pov + 8, got_address + 8); +} + // The first entry in the PLT for a shared object. -const unsigned char Output_data_plt_i386::dyn_first_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_i386_dyn::first_plt_entry[plt_entry_size] = { 0xff, 0xb3, 4, 0, 0, 0, // pushl 4(%ebx) 0xff, 0xa3, 8, 0, 0, 0, // jmp *8(%ebx) 0, 0, 0, 0 // unused }; +void +Output_data_plt_i386_dyn::do_fill_first_plt_entry( + unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr) +{ + memcpy(pov, first_plt_entry, plt_entry_size); +} + // Subsequent entries in the PLT for an executable. -const unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_i386_exec::plt_entry[plt_entry_size] = { 0xff, 0x25, // jmp indirect 0, 0, 0, 0, // replaced with address of symbol in .got @@ -903,9 +1305,25 @@ const unsigned char Output_data_plt_i386::exec_plt_entry[plt_entry_size] = 0, 0, 0, 0 // replaced with offset to start of .plt }; +unsigned int +Output_data_plt_i386_exec::do_fill_plt_entry( + unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset) +{ + memcpy(pov, plt_entry, plt_entry_size); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, + got_address + got_offset); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_rel_offset); + elfcpp::Swap<32, false>::writeval(pov + 12, - (plt_offset + 12 + 4)); + return 6; +} + // Subsequent entries in the PLT for a shared object. -const unsigned char Output_data_plt_i386::dyn_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_i386_dyn::plt_entry[plt_entry_size] = { 0xff, 0xa3, // jmp *offset(%ebx) 0, 0, 0, 0, // replaced with offset of symbol in .got @@ -915,6 +1333,20 @@ const unsigned char Output_data_plt_i386::dyn_plt_entry[plt_entry_size] = 0, 0, 0, 0 // replaced with offset to start of .plt }; +unsigned int +Output_data_plt_i386_dyn::do_fill_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset) +{ + memcpy(pov, plt_entry, plt_entry_size); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_offset); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_rel_offset); + elfcpp::Swap<32, false>::writeval(pov + 12, - (plt_offset + 12 + 4)); + return 6; +} + // The .eh_frame unwind information for the PLT. const unsigned char @@ -937,7 +1369,7 @@ Output_data_plt_i386::plt_eh_frame_cie[plt_eh_frame_cie_size] = }; const unsigned char -Output_data_plt_i386::plt_eh_frame_fde[plt_eh_frame_fde_size] = +Output_data_plt_i386_standard::plt_eh_frame_fde[plt_eh_frame_fde_size] = { 0, 0, 0, 0, // Replaced with offset to .plt. 0, 0, 0, 0, // Replaced with size of .plt. @@ -976,8 +1408,13 @@ Output_data_plt_i386::do_write(Output_file* of) unsigned char* const oview = of->get_output_view(offset, oview_size); const off_t got_file_offset = this->got_plt_->offset(); + gold_assert(parameters->incremental_update() + || (got_file_offset + this->got_plt_->data_size() + == this->got_irelative_->offset())); const section_size_type got_size = - convert_to_section_size_type(this->got_plt_->data_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); @@ -986,63 +1423,45 @@ Output_data_plt_i386::do_write(Output_file* of) elfcpp::Elf_types<32>::Elf_Addr plt_address = this->address(); elfcpp::Elf_types<32>::Elf_Addr got_address = this->got_plt_->address(); - if (parameters->options().output_is_position_independent()) - memcpy(pov, dyn_first_plt_entry, plt_entry_size); - else - { - memcpy(pov, exec_first_plt_entry, plt_entry_size); - elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_address + 4); - elfcpp::Swap<32, false>::writeval(pov + 8, got_address + 8); - } - pov += plt_entry_size; - - unsigned char* got_pov = got_view; + this->fill_first_plt_entry(pov, got_address); + pov += this->get_plt_entry_size(); - memset(got_pov, 0, 12); - got_pov += 12; + // 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; - unsigned int plt_offset = plt_entry_size; + unsigned int plt_offset = this->get_plt_entry_size(); unsigned int plt_rel_offset = 0; unsigned int got_offset = 12; - const unsigned int count = this->count_; + const unsigned int count = this->count_ + this->irelative_count_; for (unsigned int i = 0; i < count; ++i, - pov += plt_entry_size, + pov += this->get_plt_entry_size(), got_pov += 4, - plt_offset += plt_entry_size, + plt_offset += this->get_plt_entry_size(), plt_rel_offset += rel_size, got_offset += 4) { // Set and adjust the PLT entry itself. - - if (parameters->options().output_is_position_independent()) - { - memcpy(pov, dyn_plt_entry, plt_entry_size); - elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_offset); - } - else - { - memcpy(pov, exec_plt_entry, plt_entry_size); - elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, - (got_address - + got_offset)); - } - - elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_rel_offset); - elfcpp::Swap<32, false>::writeval(pov + 12, - - (plt_offset + plt_entry_size)); + unsigned int lazy_offset = this->fill_plt_entry(pov, + got_address, + got_offset, + plt_offset, + plt_rel_offset); // Set the entry in the GOT. - elfcpp::Swap<32, false>::writeval(got_pov, plt_address + plt_offset + 6); + elfcpp::Swap<32, false>::writeval(got_pov, + plt_address + plt_offset + lazy_offset); } // If any STT_GNU_IFUNC symbols have PLT entries, we need to change // the GOT to point to the actual symbol value, rather than point to // the PLT entry. That will let the dynamic linker call the right // function when resolving IRELATIVE relocations. + unsigned char* got_irelative_view = got_view + this->got_plt_->data_size(); for (std::vector::const_iterator p = this->global_ifuncs_.begin(); p != this->global_ifuncs_.end(); @@ -1050,7 +1469,7 @@ Output_data_plt_i386::do_write(Output_file* of) { const Sized_symbol<32>* ssym = static_cast*>(p->sym); - elfcpp::Swap<32, false>::writeval(got_view + p->got_offset, + elfcpp::Swap<32, false>::writeval(got_irelative_view + p->got_offset, ssym->value()); } @@ -1061,7 +1480,7 @@ Output_data_plt_i386::do_write(Output_file* of) { const Symbol_value<32>* psymval = p->object->local_symbol(p->local_sym_index); - elfcpp::Swap<32, false>::writeval(got_view + p->got_offset, + elfcpp::Swap<32, false>::writeval(got_irelative_view + p->got_offset, psymval->value(p->object, 0)); } @@ -1082,7 +1501,16 @@ Target_i386::make_plt_section(Symbol_table* symtab, Layout* layout) // Create the GOT sections first. this->got_section(symtab, layout); - this->plt_ = new Output_data_plt_i386(symtab, layout, this->got_plt_); + const bool dyn = parameters->options().output_is_position_independent(); + this->plt_ = this->make_data_plt(layout, + this->got_plt_, + this->got_irelative_, + dyn); + + // Add unwind information if requested. + if (parameters->options().ld_generated_unwind_info()) + this->plt_->add_eh_frame(layout); + layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR), @@ -1103,7 +1531,7 @@ Target_i386::make_plt_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym) return; if (this->plt_ == NULL) this->make_plt_section(symtab, layout); - this->plt_->add_entry(gsym); + this->plt_->add_entry(symtab, layout, gsym); } // Make a PLT entry for a local STT_GNU_IFUNC symbol. @@ -1117,7 +1545,8 @@ Target_i386::make_local_ifunc_plt_entry(Symbol_table* symtab, Layout* layout, return; if (this->plt_ == NULL) this->make_plt_section(symtab, layout); - unsigned int plt_offset = this->plt_->add_local_ifunc_entry(relobj, + unsigned int plt_offset = this->plt_->add_local_ifunc_entry(symtab, layout, + relobj, local_sym_index); relobj->set_local_plt_offset(local_sym_index, plt_offset); } @@ -1137,7 +1566,9 @@ Target_i386::plt_entry_count() const unsigned int Target_i386::first_plt_entry_offset() const { - return Output_data_plt_i386::first_plt_entry_offset(); + if (this->plt_ == NULL) + return 0; + return this->plt_->first_plt_entry_offset(); } // Return the size of each PLT entry. @@ -1145,7 +1576,9 @@ Target_i386::first_plt_entry_offset() const unsigned int Target_i386::plt_entry_size() const { - return Output_data_plt_i386::get_plt_entry_size(); + if (this->plt_ == NULL) + return 0; + return this->plt_->get_plt_entry_size(); } // Get the section to use for TLS_DESC relocations. @@ -1186,7 +1619,7 @@ Target_i386::define_tls_base_symbol(Symbol_table* symtab, Layout* layout) unsigned int Target_i386::got_mod_index_entry(Symbol_table* symtab, Layout* layout, - Sized_relobj_file<32, false>* object) + Sized_relobj_file<32, false>* object) { if (this->got_mod_index_offset_ == -1U) { @@ -1195,7 +1628,7 @@ Target_i386::got_mod_index_entry(Symbol_table* symtab, Layout* layout, Output_data_got<32, false>* got = this->got_section(symtab, layout); unsigned int got_offset = got->add_constant(0); rel_dyn->add_local(object, 0, elfcpp::R_386_TLS_DTPMOD32, got, - got_offset); + got_offset); got->add_constant(0); this->got_mod_index_offset_ = got_offset; } @@ -1288,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; @@ -1350,7 +1784,7 @@ Target_i386::Scan::reloc_needs_plt_for_ifunc( int flags = Scan::get_reference_flags(r_type); if (flags & Symbol::TLS_REF) gold_error(_("%s: unsupported TLS reloc %u for IFUNC symbol"), - object->name().c_str(), r_type); + object->name().c_str(), r_type); return flags != 0; } @@ -1365,8 +1799,12 @@ Target_i386::Scan::local(Symbol_table* symtab, Output_section* output_section, const elfcpp::Rel<32, false>& reloc, unsigned int r_type, - const elfcpp::Sym<32, false>& lsym) + const elfcpp::Sym<32, false>& lsym, + bool is_discarded) { + if (is_discarded) + return; + // A local STT_GNU_IFUNC symbol may require a PLT entry. if (lsym.get_st_type() == elfcpp::STT_GNU_IFUNC && this->reloc_needs_plt_for_ifunc(object, r_type)) @@ -1390,13 +1828,13 @@ Target_i386::Scan::local(Symbol_table* symtab, // an R_386_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()); + { + 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_386_RELATIVE, output_section, data_shndx, reloc.get_r_offset()); - } + } break; case elfcpp::R_386_16: @@ -1407,15 +1845,15 @@ Target_i386::Scan::local(Symbol_table* symtab, // data section, we need to be careful not to apply this // relocation statically. if (parameters->options().output_is_position_independent()) - { - Reloc_section* rel_dyn = target->rel_dyn_section(layout); + { + Reloc_section* rel_dyn = target->rel_dyn_section(layout); unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); - if (lsym.get_st_type() != elfcpp::STT_SECTION) + if (lsym.get_st_type() != elfcpp::STT_SECTION) rel_dyn->add_local(object, r_sym, r_type, output_section, data_shndx, reloc.get_r_offset()); - else - { - gold_assert(lsym.get_st_value() == 0); + else + { + gold_assert(lsym.get_st_value() == 0); unsigned int shndx = lsym.get_st_shndx(); bool is_ordinary; shndx = object->adjust_sym_shndx(r_sym, shndx, @@ -1427,8 +1865,8 @@ Target_i386::Scan::local(Symbol_table* symtab, rel_dyn->add_local_section(object, shndx, r_type, output_section, data_shndx, reloc.get_r_offset()); - } - } + } + } break; case elfcpp::R_386_PC32: @@ -1448,10 +1886,29 @@ Target_i386::Scan::local(Symbol_table* symtab, break; case elfcpp::R_386_GOT32: + case elfcpp::R_386_GOT32X: { - // The symbol requires a GOT entry. - Output_data_got<32, false>* got = target->got_section(symtab, layout); - unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + // 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 // lets function pointers compare correctly with shared @@ -1461,20 +1918,20 @@ Target_i386::Scan::local(Symbol_table* symtab, is_new = got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD); else is_new = got->add_local(object, r_sym, GOT_TYPE_STANDARD); - if (is_new) - { - // 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); + if (is_new) + { + // 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 got_offset = object->local_got_offset(r_sym, GOT_TYPE_STANDARD); rel_dyn->add_local_relative(object, r_sym, elfcpp::R_386_RELATIVE, got, got_offset); - } - } + } + } } break; @@ -1509,28 +1966,28 @@ Target_i386::Scan::local(Symbol_table* symtab, { bool output_is_shared = parameters->options().shared(); const tls::Tls_optimization optimized_type - = Target_i386::optimize_tls_reloc(!output_is_shared, r_type); + = Target_i386::optimize_tls_reloc(!output_is_shared, r_type); switch (r_type) { case elfcpp::R_386_TLS_GD: // Global-dynamic if (optimized_type == tls::TLSOPT_NONE) { - // Create a pair of GOT entries for the module index and - // dtv-relative offset. - Output_data_got<32, false>* got - = target->got_section(symtab, layout); - unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + // Create a pair of GOT entries for the module index and + // dtv-relative offset. + Output_data_got<32, false>* got + = target->got_section(symtab, layout); + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); unsigned int shndx = lsym.get_st_shndx(); bool is_ordinary; shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary); if (!is_ordinary) object->error(_("local symbol %u has bad shndx %u"), r_sym, shndx); - else + else got->add_local_pair_with_rel(object, r_sym, shndx, GOT_TYPE_TLS_PAIR, target->rel_dyn_section(layout), - elfcpp::R_386_TLS_DTPMOD32, 0); + elfcpp::R_386_TLS_DTPMOD32); } else if (optimized_type != tls::TLSOPT_TO_LE) unsupported_reloc_local(object, r_type); @@ -1538,16 +1995,16 @@ Target_i386::Scan::local(Symbol_table* symtab, case elfcpp::R_386_TLS_GOTDESC: // Global-dynamic (from ~oliva) target->define_tls_base_symbol(symtab, layout); - if (optimized_type == tls::TLSOPT_NONE) - { - // Create a double GOT entry with an R_386_TLS_DESC - // reloc. The R_386_TLS_DESC reloc is resolved - // lazily, so the GOT entry needs to be in an area in - // .got.plt, not .got. Call got_section to make sure - // the section has been created. + if (optimized_type == tls::TLSOPT_NONE) + { + // Create a double GOT entry with an R_386_TLS_DESC + // reloc. The R_386_TLS_DESC reloc is resolved + // lazily, so the GOT entry needs to be in an area in + // .got.plt, not .got. Call got_section to make sure + // the section has been created. target->got_section(symtab, layout); - Output_data_got<32, false>* got = target->got_tlsdesc_section(); - unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + Output_data_got<32, false>* got = target->got_tlsdesc_section(); + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); if (!object->local_has_got_offset(r_sym, GOT_TYPE_TLS_DESC)) { unsigned int got_offset = got->add_constant(0); @@ -1562,9 +2019,9 @@ Target_i386::Scan::local(Symbol_table* symtab, Reloc_section* rt = target->rel_tls_desc_section(layout); rt->add_absolute(elfcpp::R_386_TLS_DESC, got, got_offset); } - } - else if (optimized_type != tls::TLSOPT_TO_LE) - unsupported_reloc_local(object, r_type); + } + else if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_local(object, r_type); break; case elfcpp::R_386_TLS_DESC_CALL: @@ -1573,8 +2030,8 @@ Target_i386::Scan::local(Symbol_table* symtab, case elfcpp::R_386_TLS_LDM: // Local-dynamic if (optimized_type == tls::TLSOPT_NONE) { - // Create a GOT entry for the module index. - target->got_mod_index_entry(symtab, layout, object); + // Create a GOT entry for the module index. + target->got_mod_index_entry(symtab, layout, object); } else if (optimized_type != tls::TLSOPT_TO_LE) unsupported_reloc_local(object, r_type); @@ -1589,32 +2046,32 @@ Target_i386::Scan::local(Symbol_table* symtab, layout->set_has_static_tls(); if (optimized_type == tls::TLSOPT_NONE) { - // For the R_386_TLS_IE relocation, we need to create a - // dynamic relocation when building a shared library. - if (r_type == elfcpp::R_386_TLS_IE - && parameters->options().shared()) - { - 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_386_RELATIVE, - output_section, data_shndx, - reloc.get_r_offset()); - } - // Create a GOT entry for the tp-relative offset. - Output_data_got<32, false>* got - = target->got_section(symtab, layout); - unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); - unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_IE_32 - ? elfcpp::R_386_TLS_TPOFF32 - : elfcpp::R_386_TLS_TPOFF); - unsigned int got_type = (r_type == elfcpp::R_386_TLS_IE_32 - ? GOT_TYPE_TLS_OFFSET - : GOT_TYPE_TLS_NOFFSET); - got->add_local_with_rel(object, r_sym, got_type, - target->rel_dyn_section(layout), - dyn_r_type); + // For the R_386_TLS_IE relocation, we need to create a + // dynamic relocation when building a shared library. + if (r_type == elfcpp::R_386_TLS_IE + && parameters->options().shared()) + { + 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_386_RELATIVE, + output_section, data_shndx, + reloc.get_r_offset()); + } + // Create a GOT entry for the tp-relative offset. + Output_data_got<32, false>* got + = target->got_section(symtab, layout); + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_IE_32 + ? elfcpp::R_386_TLS_TPOFF32 + : elfcpp::R_386_TLS_TPOFF); + unsigned int got_type = (r_type == elfcpp::R_386_TLS_IE_32 + ? GOT_TYPE_TLS_OFFSET + : GOT_TYPE_TLS_NOFFSET); + got->add_local_with_rel(object, r_sym, got_type, + target->rel_dyn_section(layout), + dyn_r_type); } else if (optimized_type != tls::TLSOPT_TO_LE) unsupported_reloc_local(object, r_type); @@ -1625,15 +2082,15 @@ Target_i386::Scan::local(Symbol_table* symtab, layout->set_has_static_tls(); if (output_is_shared) { - // We need to create a dynamic relocation. - gold_assert(lsym.get_st_type() != elfcpp::STT_SECTION); - unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); - unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_LE_32 - ? elfcpp::R_386_TLS_TPOFF32 - : elfcpp::R_386_TLS_TPOFF); - Reloc_section* rel_dyn = target->rel_dyn_section(layout); - rel_dyn->add_local(object, r_sym, dyn_r_type, output_section, - data_shndx, reloc.get_r_offset()); + // We need to create a dynamic relocation. + gold_assert(lsym.get_st_type() != elfcpp::STT_SECTION); + unsigned int r_sym = elfcpp::elf_r_sym<32>(reloc.get_r_info()); + unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_LE_32 + ? elfcpp::R_386_TLS_TPOFF32 + : elfcpp::R_386_TLS_TPOFF); + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + rel_dyn->add_local(object, r_sym, dyn_r_type, output_section, + data_shndx, reloc.get_r_offset()); } break; @@ -1681,8 +2138,9 @@ 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; + return true; } default: return false; @@ -1724,14 +2182,14 @@ Target_i386::Scan::global_reloc_may_be_function_pointer( inline void Target_i386::Scan::global(Symbol_table* symtab, - Layout* layout, - Target_i386* target, - Sized_relobj_file<32, false>* object, - unsigned int data_shndx, - Output_section* output_section, - const elfcpp::Rel<32, false>& reloc, - unsigned int r_type, - Symbol* gsym) + Layout* layout, + Target_i386* target, + Sized_relobj_file<32, false>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rel<32, false>& reloc, + unsigned int r_type, + Symbol* gsym) { // A STT_GNU_IFUNC symbol may require a PLT entry. if (gsym->type() == elfcpp::STT_GNU_IFUNC @@ -1749,25 +2207,26 @@ Target_i386::Scan::global(Symbol_table* symtab, case elfcpp::R_386_16: case elfcpp::R_386_8: { - // Make a PLT entry if necessary. - if (gsym->needs_plt_entry()) - { - target->make_plt_entry(symtab, layout, gsym); - // Since this is not a PC-relative relocation, we may be - // taking the address of a function. In that case we need to - // set the entry in the dynamic symbol table to the address of - // the PLT entry. - if (gsym->is_from_dynobj() && !parameters->options().shared()) - gsym->set_needs_dynsym_value(); - } - // Make a dynamic relocation if necessary. - if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))) - { - if (gsym->may_need_copy_reloc()) - { - target->copy_reloc(symtab, layout, object, - data_shndx, output_section, gsym, reloc); - } + // Make a PLT entry if necessary. + if (gsym->needs_plt_entry()) + { + target->make_plt_entry(symtab, layout, gsym); + // Since this is not a PC-relative relocation, we may be + // taking the address of a function. In that case we need to + // set the entry in the dynamic symbol table to the address of + // the PLT entry. + if (gsym->is_from_dynobj() && !parameters->options().shared()) + gsym->set_needs_dynsym_value(); + } + // Make a dynamic relocation if necessary. + if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))) + { + if (!parameters->options().output_is_position_independent() + && gsym->may_need_copy_reloc()) + { + target->copy_reloc(symtab, layout, object, + data_shndx, output_section, gsym, reloc); + } else if (r_type == elfcpp::R_386_32 && gsym->type() == elfcpp::STT_GNU_IFUNC && gsym->can_use_relative_reloc(false) @@ -1779,28 +2238,28 @@ Target_i386::Scan::global(Symbol_table* symtab, // STT_GNU_IFUNC symbol. This makes a function // address in a PIE executable match the address in a // shared library that it links against. - Reloc_section* rel_dyn = target->rel_dyn_section(layout); + Reloc_section* rel_dyn = target->rel_irelative_section(layout); rel_dyn->add_symbolless_global_addend(gsym, elfcpp::R_386_IRELATIVE, output_section, object, data_shndx, reloc.get_r_offset()); } - else if (r_type == elfcpp::R_386_32 - && gsym->can_use_relative_reloc(false)) - { - Reloc_section* rel_dyn = target->rel_dyn_section(layout); + else if (r_type == elfcpp::R_386_32 + && gsym->can_use_relative_reloc(false)) + { + Reloc_section* rel_dyn = target->rel_dyn_section(layout); rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE, output_section, object, data_shndx, reloc.get_r_offset()); - } - else - { - 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()); - } - } + } + else + { + 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; @@ -1808,42 +2267,60 @@ Target_i386::Scan::global(Symbol_table* symtab, case elfcpp::R_386_PC16: case elfcpp::R_386_PC8: { - // Make a PLT entry if necessary. - if (gsym->needs_plt_entry()) - { - // These relocations are used for function calls only in - // non-PIC code. For a 32-bit relocation in a shared library, - // we'll need a text relocation anyway, so we can skip the - // PLT entry and let the dynamic linker bind the call directly - // to the target. For smaller relocations, we should use a - // PLT entry to ensure that the call can reach. - if (!parameters->options().shared() - || r_type != elfcpp::R_386_PC32) - target->make_plt_entry(symtab, layout, gsym); - } - // Make a dynamic relocation if necessary. - if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))) - { - if (gsym->may_need_copy_reloc()) - { - target->copy_reloc(symtab, layout, object, - data_shndx, output_section, gsym, reloc); - } - else - { - 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()); - } - } + // Make a PLT entry if necessary. + if (gsym->needs_plt_entry()) + { + // These relocations are used for function calls only in + // non-PIC code. For a 32-bit relocation in a shared library, + // we'll need a text relocation anyway, so we can skip the + // PLT entry and let the dynamic linker bind the call directly + // to the target. For smaller relocations, we should use a + // PLT entry to ensure that the call can reach. + if (!parameters->options().shared() + || r_type != elfcpp::R_386_PC32) + target->make_plt_entry(symtab, layout, gsym); + } + // Make a dynamic relocation if necessary. + if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))) + { + if (parameters->options().output_is_executable() + && gsym->may_need_copy_reloc()) + { + target->copy_reloc(symtab, layout, object, + data_shndx, output_section, gsym, reloc); + } + else + { + 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_386_GOT32: + case elfcpp::R_386_GOT32X: { - // The symbol requires a GOT entry. - Output_data_got<32, false>* got = target->got_section(symtab, layout); - if (gsym->final_value_is_known()) + // 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. if (gsym->type() == elfcpp::STT_GNU_IFUNC) @@ -1851,20 +2328,35 @@ Target_i386::Scan::global(Symbol_table* symtab, else 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() + 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); + + // Use a GLOB_DAT rather than a RELATIVE reloc if: + // + // 1) The symbol may be defined in some other module. + // + // 2) We are building a shared library and this is a + // protected symbol; using GLOB_DAT means that the dynamic + // linker can use the address of the PLT in the main + // executable when appropriate so that function address + // comparisons work. + // + // 3) This is a STT_GNU_IFUNC symbol in position dependent + // code, again so that function address comparisons work. + if (gsym->is_from_dynobj() + || gsym->is_undefined() + || gsym->is_preemptible() + || (gsym->visibility() == elfcpp::STV_PROTECTED + && parameters->options().shared()) || (gsym->type() == elfcpp::STT_GNU_IFUNC && parameters->options().output_is_position_independent())) - got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, - rel_dyn, elfcpp::R_386_GLOB_DAT); - else - { + got->add_global_with_rel(gsym, GOT_TYPE_STANDARD, + rel_dyn, elfcpp::R_386_GLOB_DAT); + else + { // For a STT_GNU_IFUNC symbol we want to write the PLT // offset into the GOT, so that function pointer // comparisons work correctly. @@ -1880,14 +2372,14 @@ Target_i386::Scan::global(Symbol_table* symtab, && !parameters->options().shared()) gsym->set_needs_dynsym_value(); } - if (is_new) + if (is_new) { unsigned int got_off = gsym->got_offset(GOT_TYPE_STANDARD); rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE, got, got_off); } - } - } + } + } } break; @@ -1900,13 +2392,30 @@ Target_i386::Scan::global(Symbol_table* symtab, // 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()) + && !gsym->is_from_dynobj() + && !gsym->is_preemptible()) break; target->make_plt_entry(symtab, layout, gsym); 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); @@ -1943,29 +2452,29 @@ Target_i386::Scan::global(Symbol_table* symtab, { const bool is_final = gsym->final_value_is_known(); const tls::Tls_optimization optimized_type - = Target_i386::optimize_tls_reloc(is_final, r_type); + = Target_i386::optimize_tls_reloc(is_final, r_type); switch (r_type) { case elfcpp::R_386_TLS_GD: // Global-dynamic if (optimized_type == tls::TLSOPT_NONE) { - // Create a pair of GOT entries for the module index and - // dtv-relative offset. - Output_data_got<32, false>* got - = target->got_section(symtab, layout); - got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_PAIR, - target->rel_dyn_section(layout), - elfcpp::R_386_TLS_DTPMOD32, - elfcpp::R_386_TLS_DTPOFF32); + // Create a pair of GOT entries for the module index and + // dtv-relative offset. + Output_data_got<32, false>* got + = target->got_section(symtab, layout); + got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_PAIR, + target->rel_dyn_section(layout), + elfcpp::R_386_TLS_DTPMOD32, + elfcpp::R_386_TLS_DTPOFF32); } else if (optimized_type == tls::TLSOPT_TO_IE) { - // Create a GOT entry for the tp-relative offset. - Output_data_got<32, false>* got - = target->got_section(symtab, layout); - got->add_global_with_rel(gsym, GOT_TYPE_TLS_NOFFSET, - target->rel_dyn_section(layout), - elfcpp::R_386_TLS_TPOFF); + // Create a GOT entry for the tp-relative offset. + Output_data_got<32, false>* got + = target->got_section(symtab, layout); + got->add_global_with_rel(gsym, GOT_TYPE_TLS_NOFFSET, + target->rel_dyn_section(layout), + elfcpp::R_386_TLS_TPOFF); } else if (optimized_type != tls::TLSOPT_TO_LE) unsupported_reloc_global(object, r_type, gsym); @@ -1973,30 +2482,30 @@ Target_i386::Scan::global(Symbol_table* symtab, case elfcpp::R_386_TLS_GOTDESC: // Global-dynamic (~oliva url) target->define_tls_base_symbol(symtab, layout); - if (optimized_type == tls::TLSOPT_NONE) - { - // Create a double GOT entry with an R_386_TLS_DESC - // reloc. The R_386_TLS_DESC reloc is resolved - // lazily, so the GOT entry needs to be in an area in - // .got.plt, not .got. Call got_section to make sure - // the section has been created. + if (optimized_type == tls::TLSOPT_NONE) + { + // Create a double GOT entry with an R_386_TLS_DESC + // reloc. The R_386_TLS_DESC reloc is resolved + // lazily, so the GOT entry needs to be in an area in + // .got.plt, not .got. Call got_section to make sure + // the section has been created. target->got_section(symtab, layout); - Output_data_got<32, false>* got = target->got_tlsdesc_section(); + Output_data_got<32, false>* got = target->got_tlsdesc_section(); Reloc_section* rt = target->rel_tls_desc_section(layout); - got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_DESC, rt, - elfcpp::R_386_TLS_DESC, 0); - } - else if (optimized_type == tls::TLSOPT_TO_IE) - { - // Create a GOT entry for the tp-relative offset. - Output_data_got<32, false>* got - = target->got_section(symtab, layout); - got->add_global_with_rel(gsym, GOT_TYPE_TLS_NOFFSET, - target->rel_dyn_section(layout), - elfcpp::R_386_TLS_TPOFF); - } - else if (optimized_type != tls::TLSOPT_TO_LE) - unsupported_reloc_global(object, r_type, gsym); + got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_DESC, rt, + elfcpp::R_386_TLS_DESC, 0); + } + else if (optimized_type == tls::TLSOPT_TO_IE) + { + // Create a GOT entry for the tp-relative offset. + Output_data_got<32, false>* got + = target->got_section(symtab, layout); + got->add_global_with_rel(gsym, GOT_TYPE_TLS_NOFFSET, + target->rel_dyn_section(layout), + elfcpp::R_386_TLS_TPOFF); + } + else if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_global(object, r_type, gsym); break; case elfcpp::R_386_TLS_DESC_CALL: @@ -2005,8 +2514,8 @@ Target_i386::Scan::global(Symbol_table* symtab, case elfcpp::R_386_TLS_LDM: // Local-dynamic if (optimized_type == tls::TLSOPT_NONE) { - // Create a GOT entry for the module index. - target->got_mod_index_entry(symtab, layout, object); + // Create a GOT entry for the module index. + target->got_mod_index_entry(symtab, layout, object); } else if (optimized_type != tls::TLSOPT_TO_LE) unsupported_reloc_global(object, r_type, gsym); @@ -2021,29 +2530,29 @@ Target_i386::Scan::global(Symbol_table* symtab, layout->set_has_static_tls(); if (optimized_type == tls::TLSOPT_NONE) { - // For the R_386_TLS_IE relocation, we need to create a - // dynamic relocation when building a shared library. - if (r_type == elfcpp::R_386_TLS_IE - && parameters->options().shared()) - { - Reloc_section* rel_dyn = target->rel_dyn_section(layout); - rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE, - output_section, object, - data_shndx, - reloc.get_r_offset()); - } - // Create a GOT entry for the tp-relative offset. - Output_data_got<32, false>* got - = target->got_section(symtab, layout); - unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_IE_32 - ? elfcpp::R_386_TLS_TPOFF32 - : elfcpp::R_386_TLS_TPOFF); - unsigned int got_type = (r_type == elfcpp::R_386_TLS_IE_32 - ? GOT_TYPE_TLS_OFFSET - : GOT_TYPE_TLS_NOFFSET); - got->add_global_with_rel(gsym, got_type, - target->rel_dyn_section(layout), - dyn_r_type); + // For the R_386_TLS_IE relocation, we need to create a + // dynamic relocation when building a shared library. + if (r_type == elfcpp::R_386_TLS_IE + && parameters->options().shared()) + { + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + rel_dyn->add_global_relative(gsym, elfcpp::R_386_RELATIVE, + output_section, object, + data_shndx, + reloc.get_r_offset()); + } + // Create a GOT entry for the tp-relative offset. + Output_data_got<32, false>* got + = target->got_section(symtab, layout); + unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_IE_32 + ? elfcpp::R_386_TLS_TPOFF32 + : elfcpp::R_386_TLS_TPOFF); + unsigned int got_type = (r_type == elfcpp::R_386_TLS_IE_32 + ? GOT_TYPE_TLS_OFFSET + : GOT_TYPE_TLS_NOFFSET); + got->add_global_with_rel(gsym, got_type, + target->rel_dyn_section(layout), + dyn_r_type); } else if (optimized_type != tls::TLSOPT_TO_LE) unsupported_reloc_global(object, r_type, gsym); @@ -2054,13 +2563,13 @@ Target_i386::Scan::global(Symbol_table* symtab, layout->set_has_static_tls(); if (parameters->options().shared()) { - // We need to create a dynamic relocation. - unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_LE_32 - ? elfcpp::R_386_TLS_TPOFF32 - : elfcpp::R_386_TLS_TPOFF); - Reloc_section* rel_dyn = target->rel_dyn_section(layout); - rel_dyn->add_global(gsym, dyn_r_type, output_section, object, - data_shndx, reloc.get_r_offset()); + // We need to create a dynamic relocation. + unsigned int dyn_r_type = (r_type == elfcpp::R_386_TLS_LE_32 + ? elfcpp::R_386_TLS_TPOFF32 + : elfcpp::R_386_TLS_TPOFF); + Reloc_section* rel_dyn = target->rel_dyn_section(layout); + rel_dyn->add_global(gsym, dyn_r_type, output_section, object, + data_shndx, reloc.get_r_offset()); } break; @@ -2090,20 +2599,18 @@ Target_i386::Scan::global(Symbol_table* symtab, void Target_i386::gc_process_relocs(Symbol_table* symtab, - Layout* layout, - Sized_relobj_file<32, false>* object, - unsigned int data_shndx, - unsigned int, - 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_symbols) -{ - gold::gc_process_relocs<32, false, Target_i386, elfcpp::SHT_REL, - Target_i386::Scan, - Target_i386::Relocatable_size_for_reloc>( + Layout* layout, + Sized_relobj_file<32, false>* object, + unsigned int data_shndx, + unsigned int, + 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_symbols) +{ + gold::gc_process_relocs<32, false, Target_i386, Scan, Classify_reloc>( symtab, layout, this, @@ -2121,16 +2628,16 @@ Target_i386::gc_process_relocs(Symbol_table* symtab, void Target_i386::scan_relocs(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_symbols) + 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_symbols) { if (sh_type == elfcpp::SHT_RELA) { @@ -2139,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, @@ -2182,7 +2688,8 @@ Target_i386::do_finalize_sections( symtab->get_sized_symbol<32>(sym)->set_symsize(data_size); } - if (parameters->doing_static_link() && this->plt_ == NULL) + if (parameters->doing_static_link() + && (this->plt_ == NULL || !this->plt_->has_irelative_section())) { // If linking statically, make sure that the __rel_iplt symbols // were defined if necessary, even if we didn't create a PLT. @@ -2231,8 +2738,8 @@ Target_i386::do_finalize_sections( inline bool Target_i386::Relocate::should_apply_static_reloc(const Sized_symbol<32>* gsym, - unsigned int r_type, - bool is_32bit, + unsigned int r_type, + bool is_32bit, Output_section* output_section) { // If the output section is not allocated, then we didn't call @@ -2249,8 +2756,8 @@ Target_i386::Relocate::should_apply_static_reloc(const Sized_symbol<32>* gsym, // (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); + && (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 @@ -2258,8 +2765,8 @@ Target_i386::Relocate::should_apply_static_reloc(const Sized_symbol<32>* gsym, // 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); + && gsym->can_use_relative_reloc(ref_flags + & Symbol::FUNCTION_CALL); return !has_dyn || is_rel; } @@ -2267,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, + unsigned int, Target_i386* target, Output_section* output_section, size_t relnum, - const elfcpp::Rel<32, false>& rel, - unsigned int r_type, + 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_PC32) + && 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; @@ -2293,6 +2807,9 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, } } + if (view == NULL) + return true; + const Sized_relobj_file<32, false>* object = relinfo->object; // Pick the value to use for symbols defined in shared objects. @@ -2312,8 +2829,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, else if (gsym != NULL && gsym->use_plt_offset(Scan::get_reference_flags(r_type))) { - symval.set_output_value(target->plt_section()->address() - + gsym->plt_offset()); + symval.set_output_value(target->plt_address_for_global(gsym)); psymval = &symval; } else if (gsym == NULL && psymval->is_ifunc_symbol()) @@ -2321,40 +2837,12 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, unsigned int r_sym = elfcpp::elf_r_sym<32>(rel.get_r_info()); if (object->local_has_plt_offset(r_sym)) { - symval.set_output_value(target->plt_section()->address() - + object->local_plt_offset(r_sym)); + symval.set_output_value(target->plt_address_for_local(object, r_sym)); psymval = &symval; } } - // 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) { @@ -2365,32 +2853,32 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, case elfcpp::R_386_32: if (should_apply_static_reloc(gsym, r_type, true, output_section)) - Relocate_functions<32, false>::rel32(view, object, psymval); + Relocate_functions<32, false>::rel32(view, object, psymval); break; case elfcpp::R_386_PC32: if (should_apply_static_reloc(gsym, r_type, true, output_section)) - Relocate_functions<32, false>::pcrel32(view, object, psymval, address); + Relocate_functions<32, false>::pcrel32(view, object, psymval, address); break; case elfcpp::R_386_16: if (should_apply_static_reloc(gsym, r_type, false, output_section)) - Relocate_functions<32, false>::rel16(view, object, psymval); + Relocate_functions<32, false>::rel16(view, object, psymval); break; case elfcpp::R_386_PC16: if (should_apply_static_reloc(gsym, r_type, false, output_section)) - Relocate_functions<32, false>::pcrel16(view, object, psymval, address); + Relocate_functions<32, false>::pcrel16(view, object, psymval, address); break; case elfcpp::R_386_8: if (should_apply_static_reloc(gsym, r_type, false, output_section)) - Relocate_functions<32, false>::rel8(view, object, psymval); + Relocate_functions<32, false>::rel8(view, object, psymval); break; case elfcpp::R_386_PC8: if (should_apply_static_reloc(gsym, r_type, false, output_section)) - Relocate_functions<32, false>::pcrel8(view, object, psymval, address); + Relocate_functions<32, false>::pcrel8(view, object, psymval, address); break; case elfcpp::R_386_PLT32: @@ -2404,16 +2892,74 @@ 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: { - elfcpp::Elf_types<32>::Elf_Addr value; - value = (psymval->value(object, 0) - - target->got_plt_section()->address()); - Relocate_functions<32, false>::rel32(view, value); + elfcpp::Elf_types<32>::Elf_Addr reladdr; + reladdr = target->got_plt_section()->address(); + Relocate_functions<32, false>::pcrel32(view, object, psymval, reladdr); } break; @@ -2455,7 +3001,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, case elfcpp::R_386_TLS_LE: // Local-exec case elfcpp::R_386_TLS_LE_32: this->relocate_tls(relinfo, target, relnum, rel, r_type, gsym, psymval, - view, address, view_size); + view, address, view_size); break; case elfcpp::R_386_32PLT: @@ -2482,7 +3028,7 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, inline void Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, - Target_i386* target, + Target_i386* target, size_t relnum, const elfcpp::Rel<32, false>& rel, unsigned int r_type, @@ -2508,45 +3054,49 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, case elfcpp::R_386_TLS_GD: // Global-dynamic if (optimized_type == tls::TLSOPT_TO_LE) { - gold_assert(tls_segment != NULL); + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return; + } this->tls_gd_to_le(relinfo, relnum, tls_segment, rel, r_type, value, view, view_size); break; } else - { - unsigned int got_type = (optimized_type == tls::TLSOPT_TO_IE - ? GOT_TYPE_TLS_NOFFSET - : GOT_TYPE_TLS_PAIR); - unsigned int got_offset; - if (gsym != NULL) - { - gold_assert(gsym->has_got_offset(got_type)); - got_offset = gsym->got_offset(got_type) - 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)); - got_offset = (object->local_got_offset(r_sym, got_type) + { + unsigned int got_type = (optimized_type == tls::TLSOPT_TO_IE + ? GOT_TYPE_TLS_NOFFSET + : GOT_TYPE_TLS_PAIR); + unsigned int got_offset; + if (gsym != NULL) + { + gold_assert(gsym->has_got_offset(got_type)); + got_offset = gsym->got_offset(got_type) - 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)); + got_offset = (object->local_got_offset(r_sym, got_type) - target->got_size()); - } - if (optimized_type == tls::TLSOPT_TO_IE) + } + if (optimized_type == tls::TLSOPT_TO_IE) + { + this->tls_gd_to_ie(relinfo, relnum, rel, r_type, + got_offset, view, view_size); + break; + } + else if (optimized_type == tls::TLSOPT_NONE) { - gold_assert(tls_segment != NULL); - this->tls_gd_to_ie(relinfo, relnum, tls_segment, rel, r_type, - got_offset, view, view_size); - break; + // Relocate the field with the offset of the pair of GOT + // entries. + Relocate_functions<32, false>::rel32(view, got_offset); + break; } - else if (optimized_type == tls::TLSOPT_NONE) - { - // Relocate the field with the offset of the pair of GOT - // entries. - Relocate_functions<32, false>::rel32(view, got_offset); - break; - } - } + } gold_error_at_location(relinfo, relnum, rel.get_r_offset(), _("unsupported reloc %u"), r_type); @@ -2556,19 +3106,24 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, case elfcpp::R_386_TLS_DESC_CALL: this->local_dynamic_type_ = LOCAL_DYNAMIC_GNU; if (optimized_type == tls::TLSOPT_TO_LE) - { - gold_assert(tls_segment != NULL); + { + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return; + } this->tls_desc_gd_to_le(relinfo, relnum, tls_segment, - rel, r_type, value, view, - view_size); + rel, r_type, value, view, + view_size); break; - } + } else - { - unsigned int got_type = (optimized_type == tls::TLSOPT_TO_IE - ? GOT_TYPE_TLS_NOFFSET - : GOT_TYPE_TLS_DESC); - unsigned int got_offset = 0; + { + unsigned int got_type = (optimized_type == tls::TLSOPT_TO_IE + ? GOT_TYPE_TLS_NOFFSET + : GOT_TYPE_TLS_DESC); + unsigned int got_offset = 0; if (r_type == elfcpp::R_386_TLS_GOTDESC && optimized_type == tls::TLSOPT_NONE) { @@ -2578,36 +3133,35 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, got_offset = (target->got_size() + target->got_plt_section()->data_size()); } - if (gsym != NULL) - { - gold_assert(gsym->has_got_offset(got_type)); - got_offset += gsym->got_offset(got_type) - 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)); - got_offset += (object->local_got_offset(r_sym, got_type) + if (gsym != NULL) + { + gold_assert(gsym->has_got_offset(got_type)); + got_offset += gsym->got_offset(got_type) - 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)); + got_offset += (object->local_got_offset(r_sym, got_type) - target->got_size()); - } - if (optimized_type == tls::TLSOPT_TO_IE) + } + if (optimized_type == tls::TLSOPT_TO_IE) { - gold_assert(tls_segment != NULL); - this->tls_desc_gd_to_ie(relinfo, relnum, tls_segment, rel, r_type, - got_offset, view, view_size); - break; + this->tls_desc_gd_to_ie(relinfo, relnum, rel, r_type, + got_offset, view, view_size); + break; } - else if (optimized_type == tls::TLSOPT_NONE) - { - if (r_type == elfcpp::R_386_TLS_GOTDESC) - { - // Relocate the field with the offset of the pair of GOT - // entries. - Relocate_functions<32, false>::rel32(view, got_offset); - } - break; - } - } + else if (optimized_type == tls::TLSOPT_NONE) + { + if (r_type == elfcpp::R_386_TLS_GOTDESC) + { + // Relocate the field with the offset of the pair of GOT + // entries. + Relocate_functions<32, false>::rel32(view, got_offset); + } + break; + } + } gold_error_at_location(relinfo, relnum, rel.get_r_offset(), _("unsupported reloc %u"), r_type); @@ -2624,21 +3178,26 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, this->local_dynamic_type_ = LOCAL_DYNAMIC_GNU; if (optimized_type == tls::TLSOPT_TO_LE) { - gold_assert(tls_segment != NULL); + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return; + } this->tls_ld_to_le(relinfo, relnum, tls_segment, rel, r_type, value, view, view_size); break; } else if (optimized_type == tls::TLSOPT_NONE) - { - // Relocate the field with the offset of the GOT entry for - // the module index. - unsigned int got_offset; - got_offset = (target->got_mod_index_entry(NULL, NULL, NULL) + { + // Relocate the field with the offset of the GOT entry for + // the module index. + unsigned int got_offset; + got_offset = (target->got_mod_index_entry(NULL, NULL, NULL) - target->got_size()); - Relocate_functions<32, false>::rel32(view, got_offset); - break; - } + Relocate_functions<32, false>::rel32(view, got_offset); + break; + } gold_error_at_location(relinfo, relnum, rel.get_r_offset(), _("unsupported reloc %u"), r_type); @@ -2655,7 +3214,12 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, elfcpp::Shdr<32, false> shdr(relinfo->data_shdr); if ((shdr.get_sh_flags() & elfcpp::SHF_EXECINSTR) != 0) { - gold_assert(tls_segment != NULL); + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return; + } value -= tls_segment->memsz(); } } @@ -2667,40 +3231,45 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, case elfcpp::R_386_TLS_IE_32: if (optimized_type == tls::TLSOPT_TO_LE) { - gold_assert(tls_segment != NULL); + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return; + } Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment, rel, r_type, value, view, view_size); break; } else if (optimized_type == tls::TLSOPT_NONE) - { - // Relocate the field with the offset of the GOT entry for - // the tp-relative offset of the symbol. + { + // Relocate the field with the offset of the GOT entry for + // the tp-relative offset of the symbol. unsigned int got_type = (r_type == elfcpp::R_386_TLS_IE_32 - ? GOT_TYPE_TLS_OFFSET - : GOT_TYPE_TLS_NOFFSET); - unsigned int got_offset; - if (gsym != NULL) - { - gold_assert(gsym->has_got_offset(got_type)); - got_offset = gsym->got_offset(got_type); - } - 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)); - got_offset = object->local_got_offset(r_sym, got_type); - } - // For the R_386_TLS_IE relocation, we need to apply the - // absolute address of the GOT entry. - if (r_type == elfcpp::R_386_TLS_IE) - got_offset += target->got_plt_section()->address(); - // All GOT offsets are relative to the end of the GOT. - got_offset -= target->got_size(); - Relocate_functions<32, false>::rel32(view, got_offset); - break; - } + ? GOT_TYPE_TLS_OFFSET + : GOT_TYPE_TLS_NOFFSET); + unsigned int got_offset; + if (gsym != NULL) + { + gold_assert(gsym->has_got_offset(got_type)); + got_offset = gsym->got_offset(got_type); + } + 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)); + got_offset = object->local_got_offset(r_sym, got_type); + } + // For the R_386_TLS_IE relocation, we need to apply the + // absolute address of the GOT entry. + if (r_type == elfcpp::R_386_TLS_IE) + got_offset += target->got_plt_section()->address(); + // All GOT offsets are relative to the end of the GOT. + got_offset -= target->got_size(); + Relocate_functions<32, false>::rel32(view, got_offset); + break; + } gold_error_at_location(relinfo, relnum, rel.get_r_offset(), _("unsupported reloc %u"), r_type); @@ -2710,22 +3279,32 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, // If we're creating a shared library, a dynamic relocation will // have been created for this location, so do not apply it now. if (!parameters->options().shared()) - { - gold_assert(tls_segment != NULL); - value -= tls_segment->memsz(); - Relocate_functions<32, false>::rel32(view, value); - } + { + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return; + } + value -= tls_segment->memsz(); + Relocate_functions<32, false>::rel32(view, value); + } break; case elfcpp::R_386_TLS_LE_32: // If we're creating a shared library, a dynamic relocation will // have been created for this location, so do not apply it now. if (!parameters->options().shared()) - { - gold_assert(tls_segment != NULL); - value = tls_segment->memsz() - value; - Relocate_functions<32, false>::rel32(view, value); - } + { + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return; + } + value = tls_segment->memsz() - value; + Relocate_functions<32, false>::rel32(view, value); + } break; } } @@ -2743,9 +3322,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); @@ -2753,10 +3334,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); + op2 == 0x8d || op2 == 0x04); + tls::check_tls(relinfo, relnum, rel.get_r_offset(), + op3 == 0xe8 || op3 == 0xff); int roff = 5; @@ -2765,17 +3348,23 @@ Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo, tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -3); 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))); + ((op1 & 0xc7) == 0x05 && op1 != (4 << 3))); memcpy(view - 3, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); } 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; } @@ -2800,59 +3389,55 @@ 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; + op2 == 0x8d || op2 == 0x04); + 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) { tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -3); 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); + ((op1 & 0xc7) == 0x05 && op1 != (4 << 3))); + 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. @@ -2881,7 +3466,7 @@ Target_i386::Relocate::tls_desc_gd_to_le( tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2); tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 4); tls::check_tls(relinfo, relnum, rel.get_r_offset(), - view[-2] == 0x8d && view[-1] == 0x83); + view[-2] == 0x8d && view[-1] == 0x83); view[-1] = 0x05; value -= tls_segment->memsz(); Relocate_functions<32, false>::rel32(view, value); @@ -2893,7 +3478,7 @@ Target_i386::Relocate::tls_desc_gd_to_le( gold_assert(r_type == elfcpp::R_386_TLS_DESC_CALL); tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 2); tls::check_tls(relinfo, relnum, rel.get_r_offset(), - view[0] == 0xff && view[1] == 0x10); + view[0] == 0xff && view[1] == 0x10); view[0] = 0x66; view[1] = 0x90; } @@ -2906,7 +3491,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, @@ -2920,7 +3504,7 @@ Target_i386::Relocate::tls_desc_gd_to_ie( tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, -2); tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 4); tls::check_tls(relinfo, relnum, rel.get_r_offset(), - view[-2] == 0x8d && view[-1] == 0x83); + view[-2] == 0x8d && view[-1] == 0x83); view[-2] = 0x8b; Relocate_functions<32, false>::rel32(view, value); } @@ -2931,7 +3515,7 @@ Target_i386::Relocate::tls_desc_gd_to_ie( gold_assert(r_type == elfcpp::R_386_TLS_DESC_CALL); tls::check_range(relinfo, relnum, rel.get_r_offset(), view_size, 2); tls::check_tls(relinfo, relnum, rel.get_r_offset(), - view[0] == 0xff && view[1] == 0x10); + view[0] == 0xff && view[1] == 0x10); view[0] = 0x66; view[1] = 0x90; } @@ -2950,19 +3534,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. @@ -3008,7 +3609,7 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo, { // movl XX,%reg ==> movl $YY,%reg tls::check_tls(relinfo, relnum, rel.get_r_offset(), - (op1 & 0xc7) == 0x05); + (op1 & 0xc7) == 0x05); view[-2] = 0xc7; view[-1] = 0xc0 | ((op1 >> 3) & 7); } @@ -3016,7 +3617,7 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo, { // addl XX,%reg ==> addl $YY,%reg tls::check_tls(relinfo, relnum, rel.get_r_offset(), - (op1 & 0xc7) == 0x05); + (op1 & 0xc7) == 0x05); view[-2] = 0x81; view[-1] = 0xc0 | ((op1 >> 3) & 7); } @@ -3035,7 +3636,7 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo, unsigned char op1 = view[-1]; unsigned char op2 = view[-2]; tls::check_tls(relinfo, relnum, rel.get_r_offset(), - (op1 & 0xc0) == 0x80 && (op1 & 7) != 4); + (op1 & 0xc0) == 0x80 && (op1 & 7) != 4); if (op2 == 0x8b) { // movl %gs:XX(%reg1),%reg2 ==> movl $YY,%reg2 @@ -3081,8 +3682,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::relocate_section<32, false, Target_i386, Relocate, + gold::Default_comdat_behavior, Classify_reloc>( relinfo, this, prelocs, @@ -3099,7 +3700,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) { @@ -3123,6 +3724,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: @@ -3183,13 +3785,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, @@ -3203,17 +3804,53 @@ Target_i386::scan_relocatable_relocs(Symbol_table* symtab, rr); } -// Relocate a section during a relocatable link. +// 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 -Target_i386::relocate_for_relocatable( +Target_i386::relocate_relocs( const Relocate_info<32, false>* relinfo, unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, Output_section* output_section, - off_t offset_in_output_section, - const Relocatable_relocs* rr, + elfcpp::Elf_types<32>::Elf_Off offset_in_output_section, unsigned char* view, elfcpp::Elf_types<32>::Elf_Addr view_address, section_size_type view_size, @@ -3222,13 +3859,12 @@ Target_i386::relocate_for_relocatable( { gold_assert(sh_type == elfcpp::SHT_REL); - gold::relocate_for_relocatable<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, @@ -3245,7 +3881,7 @@ uint64_t Target_i386::do_dynsym_value(const Symbol* gsym) const { gold_assert(gsym->is_from_dynobj() && gsym->has_plt_offset()); - return this->plt_section()->address() + gsym->plt_offset(); + return this->plt_address_for_global(gsym); } // Return a string used to fill a code section with nops to take up @@ -3261,46 +3897,55 @@ Target_i386::do_code_fill(section_size_type length) const jmp[0] = 0xe9; elfcpp::Swap_unaligned<32, false>::writeval(jmp + 1, length - 5); return (std::string(reinterpret_cast(&jmp[0]), 5) - + std::string(length - 5, '\0')); + + std::string(length - 5, static_cast(0x90))); } // Nop sequences of various lengths. - const char nop1[1] = { 0x90 }; // nop - const char nop2[2] = { 0x66, 0x90 }; // xchg %ax %ax - const char nop3[3] = { 0x8d, 0x76, 0x00 }; // leal 0(%esi),%esi - const char nop4[4] = { 0x8d, 0x74, 0x26, 0x00}; // leal 0(%esi,1),%esi - const char nop5[5] = { 0x90, 0x8d, 0x74, 0x26, // nop - 0x00 }; // leal 0(%esi,1),%esi - const char nop6[6] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi - 0x00, 0x00 }; - const char nop7[7] = { 0x8d, 0xb4, 0x26, 0x00, // leal 0L(%esi,1),%esi - 0x00, 0x00, 0x00 }; - const char nop8[8] = { 0x90, 0x8d, 0xb4, 0x26, // nop - 0x00, 0x00, 0x00, 0x00 }; // leal 0L(%esi,1),%esi - const char nop9[9] = { 0x89, 0xf6, 0x8d, 0xbc, // movl %esi,%esi - 0x27, 0x00, 0x00, 0x00, // leal 0L(%edi,1),%edi - 0x00 }; - const char nop10[10] = { 0x8d, 0x76, 0x00, 0x8d, // leal 0(%esi),%esi - 0xbc, 0x27, 0x00, 0x00, // leal 0L(%edi,1),%edi - 0x00, 0x00 }; - const char nop11[11] = { 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi - 0x8d, 0xbc, 0x27, 0x00, // leal 0L(%edi,1),%edi - 0x00, 0x00, 0x00 }; - const char nop12[12] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi - 0x00, 0x00, 0x8d, 0xbf, // leal 0L(%edi),%edi - 0x00, 0x00, 0x00, 0x00 }; - const char nop13[13] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi - 0x00, 0x00, 0x8d, 0xbc, // leal 0L(%edi,1),%edi - 0x27, 0x00, 0x00, 0x00, - 0x00 }; - const char nop14[14] = { 0x8d, 0xb4, 0x26, 0x00, // leal 0L(%esi,1),%esi - 0x00, 0x00, 0x00, 0x8d, // leal 0L(%edi,1),%edi - 0xbc, 0x27, 0x00, 0x00, - 0x00, 0x00 }; - const char nop15[15] = { 0xeb, 0x0d, 0x90, 0x90, // jmp .+15 - 0x90, 0x90, 0x90, 0x90, // nop,nop,nop,... - 0x90, 0x90, 0x90, 0x90, - 0x90, 0x90, 0x90 }; + const char nop1[1] = { '\x90' }; // nop + const char nop2[2] = { '\x66', '\x90' }; // xchg %ax %ax + const char nop3[3] = { '\x8d', '\x76', '\x00' }; // leal 0(%esi),%esi + const char nop4[4] = { '\x8d', '\x74', '\x26', // leal 0(%esi,1),%esi + '\x00'}; + const char nop5[5] = { '\x90', '\x8d', '\x74', // nop + '\x26', '\x00' }; // leal 0(%esi,1),%esi + const char nop6[6] = { '\x8d', '\xb6', '\x00', // leal 0L(%esi),%esi + '\x00', '\x00', '\x00' }; + const char nop7[7] = { '\x8d', '\xb4', '\x26', // leal 0L(%esi,1),%esi + '\x00', '\x00', '\x00', + '\x00' }; + const char nop8[8] = { '\x90', '\x8d', '\xb4', // nop + '\x26', '\x00', '\x00', // leal 0L(%esi,1),%esi + '\x00', '\x00' }; + const char nop9[9] = { '\x89', '\xf6', '\x8d', // movl %esi,%esi + '\xbc', '\x27', '\x00', // leal 0L(%edi,1),%edi + '\x00', '\x00', '\x00' }; + const char nop10[10] = { '\x8d', '\x76', '\x00', // leal 0(%esi),%esi + '\x8d', '\xbc', '\x27', // leal 0L(%edi,1),%edi + '\x00', '\x00', '\x00', + '\x00' }; + const char nop11[11] = { '\x8d', '\x74', '\x26', // leal 0(%esi,1),%esi + '\x00', '\x8d', '\xbc', // leal 0L(%edi,1),%edi + '\x27', '\x00', '\x00', + '\x00', '\x00' }; + const char nop12[12] = { '\x8d', '\xb6', '\x00', // leal 0L(%esi),%esi + '\x00', '\x00', '\x00', // leal 0L(%edi),%edi + '\x8d', '\xbf', '\x00', + '\x00', '\x00', '\x00' }; + const char nop13[13] = { '\x8d', '\xb6', '\x00', // leal 0L(%esi),%esi + '\x00', '\x00', '\x00', // leal 0L(%edi,1),%edi + '\x8d', '\xbc', '\x27', + '\x00', '\x00', '\x00', + '\x00' }; + const char nop14[14] = { '\x8d', '\xb4', '\x26', // leal 0L(%esi,1),%esi + '\x00', '\x00', '\x00', // leal 0L(%edi,1),%edi + '\x00', '\x8d', '\xbc', + '\x27', '\x00', '\x00', + '\x00', '\x00' }; + const char nop15[15] = { '\xeb', '\x0d', '\x90', // jmp .+15 + '\x90', '\x90', '\x90', // nop,nop,nop,... + '\x90', '\x90', '\x90', + '\x90', '\x90', '\x90', + '\x90', '\x90', '\x90' }; const char* nops[16] = { NULL, @@ -3331,7 +3976,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())); @@ -3344,12 +3992,14 @@ Target_i386::do_is_call_to_non_split(const Symbol* sym, unsigned int) const void Target_i386::do_calls_non_split(Relobj* object, unsigned int shndx, - section_offset_type fnoffset, - section_size_type fnsize, - unsigned char* view, - section_size_type view_size, - std::string* from, - std::string* to) const + section_offset_type fnoffset, + section_size_type fnsize, + const unsigned char*, + size_t, + unsigned char* view, + section_size_type view_size, + std::string* from, + std::string* to) const { // The function starts with a comparison of the stack pointer and a // field in the TCB. This is followed by a jump. @@ -3396,7 +4046,8 @@ Target_i386::do_calls_non_split(Relobj* object, unsigned int shndx, *to = "__morestack_non_split"; } -// The selector for i386 object files. +// The selector for i386 object files. Note this is never instantiated +// directly. It's only used in Target_selector_i386_nacl, below. class Target_selector_i386 : public Target_selector_freebsd { @@ -3412,6 +4063,400 @@ public: { return new Target_i386(); } }; -Target_selector_i386 target_selector_i386; +// NaCl variant. It uses different PLT contents. + +class Output_data_plt_i386_nacl : public Output_data_plt_i386 +{ + public: + Output_data_plt_i386_nacl(Layout* layout, + Output_data_got_plt_i386* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_i386(layout, plt_entry_size, got_plt, got_irelative) + { } + + protected: + virtual unsigned int + do_get_plt_entry_size() const + { return plt_entry_size; } + + virtual void + do_add_eh_frame(Layout* layout) + { + layout->add_eh_frame_for_plt(this, plt_eh_frame_cie, plt_eh_frame_cie_size, + plt_eh_frame_fde, plt_eh_frame_fde_size); + } + + // The size of an entry in the PLT. + static const int plt_entry_size = 64; + + // The .eh_frame unwind information for the PLT. + static const int plt_eh_frame_fde_size = 32; + static const unsigned char plt_eh_frame_fde[plt_eh_frame_fde_size]; +}; + +class Output_data_plt_i386_nacl_exec : public Output_data_plt_i386_nacl +{ +public: + Output_data_plt_i386_nacl_exec(Layout* layout, + Output_data_got_plt_i386* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_i386_nacl(layout, got_plt, got_irelative) + { } + + protected: + virtual void + do_fill_first_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address); + + virtual unsigned int + do_fill_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset); + + private: + // The first entry in the PLT for an executable. + static const unsigned char first_plt_entry[plt_entry_size]; + + // Other entries in the PLT for an executable. + static const unsigned char plt_entry[plt_entry_size]; +}; + +class Output_data_plt_i386_nacl_dyn : public Output_data_plt_i386_nacl +{ + public: + Output_data_plt_i386_nacl_dyn(Layout* layout, + Output_data_got_plt_i386* got_plt, + Output_data_space* got_irelative) + : Output_data_plt_i386_nacl(layout, got_plt, got_irelative) + { } + + protected: + virtual void + do_fill_first_plt_entry(unsigned char* pov, elfcpp::Elf_types<32>::Elf_Addr); + + virtual unsigned int + do_fill_plt_entry(unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset); + + private: + // The first entry in the PLT for a shared object. + static const unsigned char first_plt_entry[plt_entry_size]; + + // Other entries in the PLT for a shared object. + static const unsigned char plt_entry[plt_entry_size]; +}; + +class Target_i386_nacl : public Target_i386 +{ + public: + Target_i386_nacl() + : Target_i386(&i386_nacl_info) + { } + + protected: + virtual Output_data_plt_i386* + do_make_data_plt(Layout* layout, + Output_data_got_plt_i386* got_plt, + Output_data_space* got_irelative, + bool dyn) + { + if (dyn) + return new Output_data_plt_i386_nacl_dyn(layout, got_plt, got_irelative); + else + return new Output_data_plt_i386_nacl_exec(layout, got_plt, got_irelative); + } + + virtual std::string + do_code_fill(section_size_type length) const; + + private: + static const Target::Target_info i386_nacl_info; +}; + +const Target::Target_info Target_i386_nacl::i386_nacl_info = +{ + 32, // size + false, // is_big_endian + elfcpp::EM_386, // 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 + "/lib/ld-nacl-x86-32.so.1", // dynamic_linker + 0x20000, // default_text_segment_address + 0x10000, // abi_pagesize (overridable by -z max-page-size) + 0x10000, // common_pagesize (overridable by -z common-page-size) + true, // isolate_execinstr + 0x10000000, // 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 +}; + +#define NACLMASK 0xe0 // 32-byte alignment mask + +const unsigned char +Output_data_plt_i386_nacl_exec::first_plt_entry[plt_entry_size] = +{ + 0xff, 0x35, // pushl contents of memory address + 0, 0, 0, 0, // replaced with address of .got + 4 + 0x8b, 0x0d, // movl contents of address, %ecx + 0, 0, 0, 0, // replaced with address of .got + 8 + 0x83, 0xe1, NACLMASK, // andl $NACLMASK, %ecx + 0xff, 0xe1, // jmp *%ecx + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90 +}; + +void +Output_data_plt_i386_nacl_exec::do_fill_first_plt_entry( + unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address) +{ + memcpy(pov, first_plt_entry, plt_entry_size); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_address + 4); + elfcpp::Swap<32, false>::writeval(pov + 8, got_address + 8); +} + +// The first entry in the PLT for a shared object. + +const unsigned char +Output_data_plt_i386_nacl_dyn::first_plt_entry[plt_entry_size] = +{ + 0xff, 0xb3, 4, 0, 0, 0, // pushl 4(%ebx) + 0x8b, 0x4b, 0x08, // mov 0x8(%ebx), %ecx + 0x83, 0xe1, NACLMASK, // andl $NACLMASK, %ecx + 0xff, 0xe1, // jmp *%ecx + 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90, // nops + 0x90, 0x90, 0x90, 0x90, 0x90 // nops +}; + +void +Output_data_plt_i386_nacl_dyn::do_fill_first_plt_entry( + unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr) +{ + memcpy(pov, first_plt_entry, plt_entry_size); +} + +// Subsequent entries in the PLT for an executable. + +const unsigned char +Output_data_plt_i386_nacl_exec::plt_entry[plt_entry_size] = +{ + 0x8b, 0x0d, // movl contents of address, %ecx */ + 0, 0, 0, 0, // replaced with address of symbol in .got + 0x83, 0xe1, NACLMASK, // andl $NACLMASK, %ecx + 0xff, 0xe1, // jmp *%ecx + + // Pad to the next 32-byte boundary with nop instructions. + 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + + // Lazy GOT entries point here (32-byte aligned). + 0x68, // pushl immediate + 0, 0, 0, 0, // replaced with offset into relocation table + 0xe9, // jmp relative + 0, 0, 0, 0, // replaced with offset to start of .plt + + // Pad to the next 32-byte boundary with nop instructions. + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90 +}; + +unsigned int +Output_data_plt_i386_nacl_exec::do_fill_plt_entry( + unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr got_address, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset) +{ + memcpy(pov, plt_entry, plt_entry_size); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, + got_address + got_offset); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 33, plt_rel_offset); + elfcpp::Swap<32, false>::writeval(pov + 38, - (plt_offset + 38 + 4)); + return 32; +} + +// Subsequent entries in the PLT for a shared object. + +const unsigned char +Output_data_plt_i386_nacl_dyn::plt_entry[plt_entry_size] = +{ + 0x8b, 0x8b, // movl offset(%ebx), %ecx + 0, 0, 0, 0, // replaced with offset of symbol in .got + 0x83, 0xe1, 0xe0, // andl $NACLMASK, %ecx + 0xff, 0xe1, // jmp *%ecx + + // Pad to the next 32-byte boundary with nop instructions. + 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + + // Lazy GOT entries point here (32-byte aligned). + 0x68, // pushl immediate + 0, 0, 0, 0, // replaced with offset into relocation table. + 0xe9, // jmp relative + 0, 0, 0, 0, // replaced with offset to start of .plt. + + // Pad to the next 32-byte boundary with nop instructions. + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90 +}; + +unsigned int +Output_data_plt_i386_nacl_dyn::do_fill_plt_entry( + unsigned char* pov, + elfcpp::Elf_types<32>::Elf_Addr, + unsigned int got_offset, + unsigned int plt_offset, + unsigned int plt_rel_offset) +{ + memcpy(pov, plt_entry, plt_entry_size); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, got_offset); + elfcpp::Swap_unaligned<32, false>::writeval(pov + 33, plt_rel_offset); + elfcpp::Swap<32, false>::writeval(pov + 38, - (plt_offset + 38 + 4)); + return 32; +} + +const unsigned char +Output_data_plt_i386_nacl::plt_eh_frame_fde[plt_eh_frame_fde_size] = +{ + 0, 0, 0, 0, // Replaced with offset to .plt. + 0, 0, 0, 0, // Replaced with size of .plt. + 0, // Augmentation size. + elfcpp::DW_CFA_def_cfa_offset, 8, // DW_CFA_def_cfa_offset: 8. + elfcpp::DW_CFA_advance_loc + 6, // Advance 6 to __PLT__ + 6. + elfcpp::DW_CFA_def_cfa_offset, 12, // DW_CFA_def_cfa_offset: 12. + elfcpp::DW_CFA_advance_loc + 58, // Advance 58 to __PLT__ + 64. + elfcpp::DW_CFA_def_cfa_expression, // DW_CFA_def_cfa_expression. + 13, // Block length. + elfcpp::DW_OP_breg4, 4, // Push %esp + 4. + elfcpp::DW_OP_breg8, 0, // Push %eip. + elfcpp::DW_OP_const1u, 63, // Push 0x3f. + elfcpp::DW_OP_and, // & (%eip & 0x3f). + elfcpp::DW_OP_const1u, 37, // Push 0x25. + elfcpp::DW_OP_ge, // >= ((%eip & 0x3f) >= 0x25) + elfcpp::DW_OP_lit2, // Push 2. + elfcpp::DW_OP_shl, // << (((%eip & 0x3f) >= 0x25) << 2) + elfcpp::DW_OP_plus, // + ((((%eip&0x3f)>=0x25)<<2)+%esp+4 + elfcpp::DW_CFA_nop, // Align to 32 bytes. + elfcpp::DW_CFA_nop +}; + +// Return a string used to fill a code section with nops. +// For NaCl, long NOPs are only valid if they do not cross +// bundle alignment boundaries, so keep it simple with one-byte NOPs. +std::string +Target_i386_nacl::do_code_fill(section_size_type length) const +{ + return std::string(length, static_cast(0x90)); +} + +// The selector for i386-nacl object files. + +class Target_selector_i386_nacl + : public Target_selector_nacl +{ + public: + Target_selector_i386_nacl() + : Target_selector_nacl("x86-32", + "elf32-i386-nacl", + "elf_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.