diff options
-rw-r--r-- | gold/ChangeLog | 57 | ||||
-rw-r--r-- | gold/i386.cc | 70 | ||||
-rw-r--r-- | gold/layout.cc | 7 | ||||
-rw-r--r-- | gold/output.cc | 84 | ||||
-rw-r--r-- | gold/output.h | 143 | ||||
-rw-r--r-- | gold/target.h | 22 | ||||
-rw-r--r-- | gold/x86_64.cc | 141 |
7 files changed, 477 insertions, 47 deletions
diff --git a/gold/ChangeLog b/gold/ChangeLog index 928af4c..038a278 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,60 @@ +2010-01-08 Ian Lance Taylor <iant@google.com> + + PR 10287 + PR 11063 + * i386.cc (class Target_i386): Change return type of plt_section + to be non-const. + (class Output_data_plt_i386): Add tls_desc_rel_ field. + (Output_data_plt_i386::Output_data_plt_i386): Initialize + tls_desc_rel_ field. + (Output_data_plt_i386::rel_tls_desc): New function. + (Target_i386::rel_tls_desc_section): New function. + (Target_i386::Scan::local): Rewrite R_386_TLS_GOTDESC handling. + (Target_i386::Scan::global): For R_386_TLS_GOTDESC put + R_386_TLS_DESC reloc in rel_tls_desc_section. + * x86_64.cc (class Target_x86_64): Add tlsdesc_reloc_info_ field. + Define struct Tlsdesc_info. + (Target_x86_64::Target_x86_64): Initialize tlsdesc_reloc_info_. + (Target_x86_64::do_reloc_symbol_index): New function. + (Target_x86_64::add_tlsdesc_info): New function. + (class Output_data_plt_x86_64): Add tlsdesc_rel_ field. + (Output_data_plt_x86_64::Output_data_plt_x86_64): Initialize + tlsdesc_rel_ field. + (Output_data_plt_x86_64::rela_plt): Rename from rel_plt. Change + all callers. + (Output_data_plt_x86_64::rela_tlsdesc): New function. + (Target_x86_64::rela_tlsdesc_section): New function. + (Target_x86_64::Scan::local): Rewrite R_X86_64_GOTPC32_TLSDESC + handling. + (Target_x86_64::Scan::global): For R_X86_64_GOTPC32_TLSDESC put + (Target_x86_64::do_reloc_addend): New function. + R_X86_64_TLSDESC reloc in rela_tlsdesc_section. + * output.h (class Output_reloc) [SHT_REL]: Add new constructor + declarations. Define TARGET_CODE. Add arg field to u1_ union. + (Output_reloc::type): New function. + (Output_reloc::is_local_section_symbol): Check for TARGET_CODE. + (Output_reloc::is_target_specific): New function. + (Output_reloc::target_arg): New function. + (class Output_reloc) [SHT_RELA]: Add four new constructors for + absolute relocs and target specific relocs. + (class Output_data_reloc) [SHT_REL]: Add add_absolute and + add_target_specific. + (class Output_data_reloc) [SHT_RELA]: Likewise. + * output.cc (Output_reloc::Output_reloc): Add four new versions + for absolute relocs and target specific relocs. + (Output_reloc::set_needs_dynsym_index): Add TARGET_CODE case. + (Output_reloc::get_symbol_index): Likewise. + (Output_reloc::local_section_offset): Check that local_sym_index_ + is not TARGET_CODE or 0. + (Output_reloc::symbol_value): Likewise. + (Output_reloc::write) [SHT_RELA]: Call target for target specific + reloc. + * target.h (class Target): Add reloc_symbol_index and reloc_addend + functions. Add do_reloc_symbol_index and do_reloc_addend virtual + functions. + * layout.cc (add_target_dynamic_tags): Use output section for + DT_PLTRELSZ and DT_JMPREL. + 2010-01-07 Ian Lance Taylor <iant@google.com> PR 11061 diff --git a/gold/i386.cc b/gold/i386.cc index e1b32e7..2eab3f8 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -368,7 +368,7 @@ class Target_i386 : public Target_freebsd<32, false> Sized_relobj<32, false>* object); // Get the PLT section. - const Output_data_plt_i386* + Output_data_plt_i386* plt_section() const { gold_assert(this->plt_ != NULL); @@ -379,6 +379,10 @@ class Target_i386 : public Target_freebsd<32, false> Reloc_section* rel_dyn_section(Layout*); + // Get the section to use for TLS_DESC relocations. + Reloc_section* + rel_tls_desc_section(Layout*) const; + // Add a potential copy relocation. void copy_reloc(Symbol_table* symtab, Layout* layout, @@ -527,6 +531,10 @@ class Output_data_plt_i386 : public Output_section_data rel_plt() const { return this->rel_; } + // Return where the TLS_DESC relocations should go. + Reloc_section* + rel_tls_desc(Layout*); + protected: void do_adjust_output_section(Output_section* os); @@ -563,6 +571,9 @@ class Output_data_plt_i386 : public Output_section_data // The reloc section. Reloc_section* rel_; + // The TLS_DESC relocations, if necessary. These must follow the + // regular PLT relocs. + Reloc_section* tls_desc_rel_; // The .got.plt section. Output_data_space* got_plt_; // The number of PLT entries. @@ -575,7 +586,7 @@ class Output_data_plt_i386 : public Output_section_data Output_data_plt_i386::Output_data_plt_i386(Layout* layout, Output_data_space* got_plt) - : Output_section_data(4), got_plt_(got_plt), count_(0) + : Output_section_data(4), tls_desc_rel_(NULL), got_plt_(got_plt), count_(0) { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, @@ -621,6 +632,24 @@ Output_data_plt_i386::add_entry(Symbol* gsym) // appear in the relocations. } +// Return where the TLS_DESC relocations should go, creating it if +// necessary. These follow the JUMP_SLOT relocations. + +Output_data_plt_i386::Reloc_section* +Output_data_plt_i386::rel_tls_desc(Layout* layout) +{ + if (this->tls_desc_rel_ == NULL) + { + this->tls_desc_rel_ = new Reloc_section(false); + layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, + elfcpp::SHF_ALLOC, this->tls_desc_rel_, + true, false, false, false); + gold_assert(this->tls_desc_rel_->output_section() == + this->rel_->output_section()); + } + return this->tls_desc_rel_; +} + // The first entry in the PLT for an executable. unsigned char Output_data_plt_i386::exec_first_plt_entry[plt_entry_size] = @@ -771,6 +800,14 @@ Target_i386::make_plt_entry(Symbol_table* symtab, Layout* layout, Symbol* gsym) this->plt_->add_entry(gsym); } +// Get the section to use for TLS_DESC relocations. + +Target_i386::Reloc_section* +Target_i386::rel_tls_desc_section(Layout* layout) const +{ + return this->plt_section()->rel_tls_desc(layout); +} + // Define the _TLS_MODULE_BASE_ symbol in the TLS segment. void @@ -1055,17 +1092,20 @@ Target_i386::Scan::local(Symbol_table* symtab, 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 - got->add_local_pair_with_rel(object, r_sym, shndx, - GOT_TYPE_TLS_DESC, - target->rel_dyn_section(layout), - elfcpp::R_386_TLS_DESC, 0); + if (!object->local_has_got_offset(r_sym, GOT_TYPE_TLS_DESC)) + { + unsigned int got_offset = got->add_constant(0); + // The local symbol value is stored in the second + // GOT entry. + got->add_local(object, r_sym, GOT_TYPE_TLS_DESC); + // That set the GOT offset of the local symbol to + // point to the second entry, but we want it to + // point to the first. + object->set_local_got_offset(r_sym, GOT_TYPE_TLS_DESC, + got_offset); + 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); @@ -1386,8 +1426,8 @@ Target_i386::Scan::global(Symbol_table* symtab, // Create a double GOT entry with an R_386_TLS_DESC reloc. Output_data_got<32, false>* got = target->got_section(symtab, layout); - got->add_global_pair_with_rel(gsym, GOT_TYPE_TLS_DESC, - target->rel_dyn_section(layout), + 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) diff --git a/gold/layout.cc b/gold/layout.cc index dc7cd13..a865909 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -3215,7 +3215,8 @@ Layout::create_interp(const Target* target) // If PLT_GOT is not NULL, then DT_PLTGOT points to it. // If PLT_REL is not NULL, it is used for DT_PLTRELSZ, and DT_JMPREL, -// and we also set DT_PLTREL. +// and we also set DT_PLTREL. We use PLT_REL's output section, since +// some targets have multiple reloc sections in PLT_REL. // If DYN_REL is not NULL, it is used for DT_REL/DT_RELA, // DT_RELSZ/DT_RELASZ, DT_RELENT/DT_RELAENT. @@ -3238,8 +3239,8 @@ Layout::add_target_dynamic_tags(bool use_rel, const Output_data* plt_got, if (plt_rel != NULL && plt_rel->output_section() != NULL) { - odyn->add_section_size(elfcpp::DT_PLTRELSZ, plt_rel); - odyn->add_section_address(elfcpp::DT_JMPREL, plt_rel); + odyn->add_section_size(elfcpp::DT_PLTRELSZ, plt_rel->output_section()); + odyn->add_section_address(elfcpp::DT_JMPREL, plt_rel->output_section()); odyn->add_constant(elfcpp::DT_PLTREL, use_rel ? elfcpp::DT_REL : elfcpp::DT_RELA); } diff --git a/gold/output.cc b/gold/output.cc index b9ded6e..bb61d86 100644 --- a/gold/output.cc +++ b/gold/output.cc @@ -756,6 +756,72 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( os->set_needs_symtab_index(); } +// An absolute relocation. + +template<bool dynamic, int size, bool big_endian> +Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( + unsigned int type, + Output_data* od, + Address address) + : address_(address), local_sym_index_(0), type_(type), + is_relative_(false), is_section_symbol_(false), shndx_(INVALID_CODE) +{ + // this->type_ is a bitfield; make sure TYPE fits. + gold_assert(this->type_ == type); + this->u1_.relobj = NULL; + this->u2_.od = od; +} + +template<bool dynamic, int size, bool big_endian> +Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( + unsigned int type, + Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, + Address address) + : address_(address), local_sym_index_(0), type_(type), + is_relative_(false), is_section_symbol_(false), shndx_(shndx) +{ + gold_assert(shndx != INVALID_CODE); + // this->type_ is a bitfield; make sure TYPE fits. + gold_assert(this->type_ == type); + this->u1_.relobj = NULL; + this->u2_.relobj = relobj; +} + +// A target specific relocation. + +template<bool dynamic, int size, bool big_endian> +Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( + unsigned int type, + void* arg, + Output_data* od, + Address address) + : address_(address), local_sym_index_(TARGET_CODE), type_(type), + is_relative_(false), is_section_symbol_(false), shndx_(INVALID_CODE) +{ + // this->type_ is a bitfield; make sure TYPE fits. + gold_assert(this->type_ == type); + this->u1_.arg = arg; + this->u2_.od = od; +} + +template<bool dynamic, int size, bool big_endian> +Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc( + unsigned int type, + void* arg, + Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, + Address address) + : address_(address), local_sym_index_(TARGET_CODE), type_(type), + is_relative_(false), is_section_symbol_(false), shndx_(shndx) +{ + gold_assert(shndx != INVALID_CODE); + // this->type_ is a bitfield; make sure TYPE fits. + gold_assert(this->type_ == type); + this->u1_.arg = arg; + this->u2_.relobj = relobj; +} + // Record that we need a dynamic symbol index for this relocation. template<bool dynamic, int size, bool big_endian> @@ -778,6 +844,10 @@ set_needs_dynsym_index() this->u1_.os->set_needs_dynsym_index(); break; + case TARGET_CODE: + // The target must take care of this if necessary. + break; + case 0: break; @@ -822,6 +892,11 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::get_symbol_index() index = this->u1_.os->symtab_index(); break; + case TARGET_CODE: + index = parameters->target().reloc_symbol_index(this->u1_.arg, + this->type_); + break; + case 0: // Relocations without symbols use a symbol index of 0. index = 0; @@ -863,7 +938,9 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>:: { gold_assert(this->local_sym_index_ != GSYM_CODE && this->local_sym_index_ != SECTION_CODE + && this->local_sym_index_ != TARGET_CODE && this->local_sym_index_ != INVALID_CODE + && this->local_sym_index_ != 0 && this->is_section_symbol_); const unsigned int lsi = this->local_sym_index_; Output_section* os = this->u1_.relobj->output_section(lsi); @@ -942,7 +1019,9 @@ Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::symbol_value( return sym->value() + addend; } gold_assert(this->local_sym_index_ != SECTION_CODE + && this->local_sym_index_ != TARGET_CODE && this->local_sym_index_ != INVALID_CODE + && this->local_sym_index_ != 0 && !this->is_section_symbol_); const unsigned int lsi = this->local_sym_index_; const Symbol_value<size>* symval = this->u1_.relobj->local_symbol(lsi); @@ -1010,7 +1089,10 @@ Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>::write( elfcpp::Rela_write<size, big_endian> orel(pov); this->rel_.write_rel(&orel); Addend addend = this->addend_; - if (this->rel_.is_relative()) + if (this->rel_.is_target_specific()) + addend = parameters->target().reloc_addend(this->rel_.target_arg(), + this->rel_.type(), addend); + else if (this->rel_.is_relative()) addend = this->rel_.symbol_value(addend); else if (this->rel_.is_local_section_symbol()) addend = this->rel_.local_section_offset(addend); diff --git a/gold/output.h b/gold/output.h index 796575a..7a35652 100644 --- a/gold/output.h +++ b/gold/output.h @@ -1057,7 +1057,30 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> Sized_relobj<size, big_endian>* relobj, unsigned int shndx, Address address); - // Return TRUE if this is a RELATIVE relocation. + // An absolute relocation with no symbol. + + Output_reloc(unsigned int type, Output_data* od, Address address); + + Output_reloc(unsigned int type, Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, Address address); + + // A target specific relocation. The target will be called to get + // the symbol index, passing ARG. The type and offset will be set + // as for other relocation types. + + Output_reloc(unsigned int type, void* arg, Output_data* od, + Address address); + + Output_reloc(unsigned int type, void* arg, + Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, Address address); + + // Return the reloc type. + unsigned int + type() const + { return this->type_; } + + // Return whether this is a RELATIVE relocation. bool is_relative() const { return this->is_relative_; } @@ -1069,9 +1092,24 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> return (this->local_sym_index_ != GSYM_CODE && this->local_sym_index_ != SECTION_CODE && this->local_sym_index_ != INVALID_CODE + && this->local_sym_index_ != TARGET_CODE && this->is_section_symbol_); } + // Return whether this is a target specific relocation. + bool + is_target_specific() const + { return this->local_sym_index_ == TARGET_CODE; } + + // Return the argument to pass to the target for a target specific + // relocation. + void* + target_arg() const + { + gold_assert(this->local_sym_index_ == TARGET_CODE); + return this->u1_.arg; + } + // For a local section symbol, return the offset of the input // section within the output section. ADDEND is the addend being // applied to the input section. @@ -1124,8 +1162,10 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> GSYM_CODE = -1U, // Output section. SECTION_CODE = -2U, + // Target specific. + TARGET_CODE = -3U, // Invalid uninitialized entry. - INVALID_CODE = -3U + INVALID_CODE = -4U }; union @@ -1143,6 +1183,9 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> // For a relocation against an output section // (this->local_sym_index_ == SECTION_CODE), the output section. Output_section* os; + // For a target specific relocation, an argument to pass to the + // target. + void* arg; } u1_; union { @@ -1157,11 +1200,12 @@ class Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> // The address offset within the input section or the Output_data. Address address_; // This is GSYM_CODE for a global symbol, or SECTION_CODE for a - // relocation against an output section, or INVALID_CODE for an - // uninitialized value. Otherwise, for a local symbol - // (this->is_section_symbol_ is false), the local symbol index. For - // a local section symbol (this->is_section_symbol_ is true), the - // section index in the input file. + // relocation against an output section, or TARGET_CODE for a target + // specific relocation, or INVALID_CODE for an uninitialized value. + // Otherwise, for a local symbol (this->is_section_symbol_ is + // false), the local symbol index. For a local section symbol + // (this->is_section_symbol_ is true), the section index in the + // input file. unsigned int local_sym_index_; // The reloc type--a processor specific code. unsigned int type_ : 30; @@ -1237,7 +1281,34 @@ class Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> : rel_(os, type, relobj, shndx, address), addend_(addend) { } - // Return TRUE if this is a RELATIVE relocation. + // An absolute relocation with no symbol. + + Output_reloc(unsigned int type, Output_data* od, Address address, + Addend addend) + : rel_(type, od, address), addend_(addend) + { } + + Output_reloc(unsigned int type, Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, Address address, Addend addend) + : rel_(type, relobj, shndx, address), addend_(addend) + { } + + // A target specific relocation. The target will be called to get + // the symbol index and the addend, passing ARG. The type and + // offset will be set as for other relocation types. + + Output_reloc(unsigned int type, void* arg, Output_data* od, + Address address, Addend addend) + : rel_(type, arg, od, address), addend_(addend) + { } + + Output_reloc(unsigned int type, void* arg, + Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, Address address, Addend addend) + : rel_(type, arg, relobj, shndx, address), addend_(addend) + { } + + // Return whether this is a RELATIVE relocation. bool is_relative() const { return this->rel_.is_relative(); } @@ -1519,6 +1590,32 @@ class Output_data_reloc<elfcpp::SHT_REL, dynamic, size, big_endian> Sized_relobj<size, big_endian>* relobj, unsigned int shndx, Address address) { this->add(od, Output_reloc_type(os, type, relobj, shndx, address)); } + + // Add an absolute relocation. + + void + add_absolute(unsigned int type, Output_data* od, Address address) + { this->add(od, Output_reloc_type(type, od, address)); } + + void + add_absolute(unsigned int type, Output_data* od, + Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, Address address) + { this->add(od, Output_reloc_type(type, relobj, shndx, address)); } + + // Add a target specific relocation. A target which calls this must + // define the reloc_symbol_index and reloc_addend virtual functions. + + void + add_target_specific(unsigned int type, void* arg, Output_data* od, + Address address) + { this->add(od, Output_reloc_type(type, arg, od, address)); } + + void + add_target_specific(unsigned int type, void* arg, Output_data* od, + Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, Address address) + { this->add(od, Output_reloc_type(type, arg, relobj, shndx, address)); } }; // The SHT_RELA version of Output_data_reloc. @@ -1651,6 +1748,36 @@ class Output_data_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian> unsigned int shndx, Address address, Addend addend) { this->add(os, Output_reloc_type(os, type, relobj, shndx, address, addend)); } + + // Add an absolute relocation. + + void + add_absolute(unsigned int type, Output_data* od, Address address, + Addend addend) + { this->add(od, Output_reloc_type(type, od, address, addend)); } + + void + add_absolute(unsigned int type, Output_data* od, + Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, Address address, Addend addend) + { this->add(od, Output_reloc_type(type, relobj, shndx, address, addend)); } + + // Add a target specific relocation. A target which calls this must + // define the reloc_symbol_index and reloc_addend virtual functions. + + void + add_target_specific(unsigned int type, void* arg, Output_data* od, + Address address, Addend addend) + { this->add(od, Output_reloc_type(type, arg, od, address, addend)); } + + void + add_target_specific(unsigned int type, void* arg, Output_data* od, + Sized_relobj<size, big_endian>* relobj, + unsigned int shndx, Address address, Addend addend) + { + this->add(od, Output_reloc_type(type, arg, relobj, shndx, address, + addend)); + } }; // Output_relocatable_relocs represents a relocation section in a diff --git a/gold/target.h b/gold/target.h index c1c58a4..88cc973 100644 --- a/gold/target.h +++ b/gold/target.h @@ -230,6 +230,16 @@ class Target is_local_label_name(const char* name) const { return this->do_is_local_label_name(name); } + // Get the symbol index to use for a target specific reloc. + unsigned int + reloc_symbol_index(void* arg, unsigned int type) const + { return this->do_reloc_symbol_index(arg, type); } + + // Get the addend to use for a target specific reloc. + uint64_t + reloc_addend(void* arg, unsigned int type, uint64_t addend) const + { return this->do_reloc_addend(arg, type, addend); } + // A function starts at OFFSET in section SHNDX in OBJECT. That // function was compiled with -fsplit-stack, but it refers to a // function which was compiled without -fsplit-stack. VIEW is a @@ -405,6 +415,18 @@ class Target virtual bool do_is_local_label_name(const char*) const; + // Virtual function that must be overridden by a target which uses + // target specific relocations. + virtual unsigned int + do_reloc_symbol_index(void*, unsigned int) const + { gold_unreachable(); } + + // Virtual function that must be overidden by a target which uses + // target specific relocations. + virtual uint64_t + do_reloc_addend(void*, unsigned int, uint64_t) const + { gold_unreachable(); } + // Virtual function which may be overridden by the child class. virtual void do_calls_non_split(Relobj* object, unsigned int, section_offset_type, diff --git a/gold/x86_64.cc b/gold/x86_64.cc index f0aade4..585a499 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -65,7 +65,8 @@ class Target_x86_64 : public Target_freebsd<64, false> : Target_freebsd<64, false>(&x86_64_info), got_(NULL), plt_(NULL), got_plt_(NULL), global_offset_table_(NULL), rela_dyn_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY), dynbss_(NULL), - got_mod_index_offset_(-1U), tls_base_symbol_defined_(false) + got_mod_index_offset_(-1U), tlsdesc_reloc_info_(), + tls_base_symbol_defined_(false) { } // Hook for a new output section. @@ -161,6 +162,20 @@ class Target_x86_64 : public Target_freebsd<64, false> do_is_defined_by_abi(const Symbol* sym) const { return strcmp(sym->name(), "__tls_get_addr") == 0; } + // Return the symbol index to use for a target specific relocation. + // The only target specific relocation is R_X86_64_TLSDESC for a + // local symbol, which is an absolute reloc. + unsigned int + do_reloc_symbol_index(void*, unsigned int r_type) const + { + gold_assert(r_type == elfcpp::R_X86_64_TLSDESC); + return 0; + } + + // Return the addend to use for a target specific relocation. + uint64_t + do_reloc_addend(void* arg, unsigned int r_type, uint64_t addend) const; + // Adjust -fstack-split code which calls non-stack-split code. void do_calls_non_split(Relobj* object, unsigned int shndx, @@ -176,6 +191,14 @@ class Target_x86_64 : public Target_freebsd<64, false> return this->got_->data_size(); } + // Add a new reloc argument, returning the index in the vector. + size_t + add_tlsdesc_info(Sized_relobj<64, false>* object, unsigned int r_sym) + { + this->tlsdesc_reloc_info_.push_back(Tlsdesc_info(object, r_sym)); + return this->tlsdesc_reloc_info_.size() - 1; + } + private: // The class which scans relocations. class Scan @@ -379,6 +402,10 @@ class Target_x86_64 : public Target_freebsd<64, false> Reloc_section* rela_dyn_section(Layout*); + // Get the section to use for TLSDESC relocations. + Reloc_section* + rela_tlsdesc_section(Layout*) const; + // Add a potential copy relocation. void copy_reloc(Symbol_table* symtab, Layout* layout, @@ -404,6 +431,21 @@ class Target_x86_64 : public Target_freebsd<64, false> GOT_TYPE_TLS_DESC = 3 // GOT entry for TLS_DESC pair }; + // This type is used as the argument to the target specific + // relocation routines. The only target specific reloc is + // R_X86_64_TLSDESC against a local symbol. + struct Tlsdesc_info + { + Tlsdesc_info(Sized_relobj<64, false>* a_object, unsigned int a_r_sym) + : object(a_object), r_sym(a_r_sym) + { } + + // The object in which the local symbol is defined. + Sized_relobj<64, false>* object; + // The local symbol index in the object. + unsigned int r_sym; + }; + // The GOT section. Output_data_got<64, false>* got_; // The PLT section. @@ -420,6 +462,10 @@ class Target_x86_64 : public Target_freebsd<64, false> Output_data_space* dynbss_; // Offset of the GOT entry for the TLS module index. unsigned int got_mod_index_offset_; + // We handle R_X86_64_TLSDESC against a local symbol as a target + // specific relocation. Here we store the object and local symbol + // index for the relocation. + std::vector<Tlsdesc_info> tlsdesc_reloc_info_; // True if the _TLS_MODULE_BASE_ symbol has been defined. bool tls_base_symbol_defined_; }; @@ -551,11 +597,15 @@ class Output_data_plt_x86_64 : public Output_section_data get_tlsdesc_plt_offset() const { return (this->count_ + 1) * plt_entry_size; } - // Return the .rel.plt section data. + // Return the .rela.plt section data. const Reloc_section* - rel_plt() const + rela_plt() const { return this->rel_; } + // Return where the TLSDESC relocations should go. + Reloc_section* + rela_tlsdesc(Layout*); + protected: void do_adjust_output_section(Output_section* os); @@ -590,6 +640,9 @@ class Output_data_plt_x86_64 : public Output_section_data // The reloc section. Reloc_section* rel_; + // The TLSDESC relocs, if necessary. These must follow the regular + // PLT relocs. + Reloc_section* tlsdesc_rel_; // The .got section. Output_data_got<64, false>* got_; // The .got.plt section. @@ -607,8 +660,8 @@ class Output_data_plt_x86_64 : public Output_section_data Output_data_plt_x86_64::Output_data_plt_x86_64(Layout* layout, Output_data_got<64, false>* got, Output_data_space* got_plt) - : Output_section_data(8), got_(got), got_plt_(got_plt), count_(0), - tlsdesc_got_offset_(-1U) + : Output_section_data(8), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), + count_(0), tlsdesc_got_offset_(-1U) { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, @@ -652,6 +705,24 @@ Output_data_plt_x86_64::add_entry(Symbol* gsym) // appear in the relocations. } +// Return where the TLSDESC relocations should go, creating it if +// necessary. These follow the JUMP_SLOT relocations. + +Output_data_plt_x86_64::Reloc_section* +Output_data_plt_x86_64::rela_tlsdesc(Layout* layout) +{ + if (this->tlsdesc_rel_ == NULL) + { + this->tlsdesc_rel_ = new Reloc_section(false); + layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, + elfcpp::SHF_ALLOC, this->tlsdesc_rel_, + true, false, false, false); + gold_assert(this->tlsdesc_rel_->output_section() == + this->rel_->output_section()); + } + return this->tlsdesc_rel_; +} + // Set the final size. void Output_data_plt_x86_64::set_final_data_size() @@ -813,6 +884,14 @@ Target_x86_64::make_plt_section(Symbol_table* symtab, Layout* layout) } } +// Return the section for TLSDESC relocations. + +Target_x86_64::Reloc_section* +Target_x86_64::rela_tlsdesc_section(Layout* layout) const +{ + return this->plt_section()->rela_tlsdesc(layout); +} + // Create a PLT entry for a global symbol. void @@ -1199,18 +1278,21 @@ Target_x86_64::Scan::local(Symbol_table* symtab, Output_data_got<64, false>* got = target->got_section(symtab, layout); unsigned int r_sym = elfcpp::elf_r_sym<64>(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 - got->add_local_pair_with_rela(object, r_sym, - shndx, - GOT_TYPE_TLS_DESC, - target->rela_dyn_section(layout), - elfcpp::R_X86_64_TLSDESC, 0); + if (!object->local_has_got_offset(r_sym, GOT_TYPE_TLS_DESC)) + { + unsigned int got_offset = got->add_constant(0); + got->add_constant(0); + object->set_local_got_offset(r_sym, GOT_TYPE_TLS_DESC, + got_offset); + Reloc_section* rt = target->rela_tlsdesc_section(layout); + // We store the arguments we need in a vector, and + // use the index into the vector as the parameter + // to pass to the target specific routines. + uintptr_t intarg = target->add_tlsdesc_info(object, r_sym); + void* arg = reinterpret_cast<void*>(intarg); + rt->add_target_specific(elfcpp::R_X86_64_TLSDESC, arg, + got, got_offset, 0); + } } else if (optimized_type != tls::TLSOPT_TO_LE) unsupported_reloc_local(object, r_type); @@ -1505,8 +1587,8 @@ Target_x86_64::Scan::global(Symbol_table* symtab, // Create a double GOT entry with an R_X86_64_TLSDESC reloc. Output_data_got<64, false>* got = target->got_section(symtab, layout); - got->add_global_pair_with_rela(gsym, GOT_TYPE_TLS_DESC, - target->rela_dyn_section(layout), + Reloc_section *rt = target->rela_tlsdesc_section(layout); + got->add_global_pair_with_rela(gsym, GOT_TYPE_TLS_DESC, rt, elfcpp::R_X86_64_TLSDESC, 0); } else if (optimized_type == tls::TLSOPT_TO_IE) @@ -1657,7 +1739,7 @@ Target_x86_64::do_finalize_sections( { const Reloc_section* rel_plt = (this->plt_ == NULL ? NULL - : this->plt_->rel_plt()); + : this->plt_->rela_plt()); layout->add_target_dynamic_tags(false, this->got_plt_, rel_plt, this->rela_dyn_, true); @@ -2658,6 +2740,25 @@ Target_x86_64::do_code_fill(section_size_type length) const return std::string(nops[length], length); } +// Return the addend to use for a target specific relocation. The +// only target specific relocation is R_X86_64_TLSDESC for a local +// symbol. We want to set the addend is the offset of the local +// symbol in the TLS segment. + +uint64_t +Target_x86_64::do_reloc_addend(void* arg, unsigned int r_type, + uint64_t) const +{ + gold_assert(r_type == elfcpp::R_X86_64_TLSDESC); + uintptr_t intarg = reinterpret_cast<uintptr_t>(arg); + gold_assert(intarg < this->tlsdesc_reloc_info_.size()); + const Tlsdesc_info& ti(this->tlsdesc_reloc_info_[intarg]); + const Symbol_value<64>* psymval = ti.object->local_symbol(ti.r_sym); + gold_assert(psymval->is_tls_symbol()); + // The value of a TLS symbol is the offset in the TLS segment. + return psymval->value(ti.object, 0); +} + // FNOFFSET in section SHNDX in OBJECT is the start of a function // compiled with -fstack-split. The function calls non-stack-split // code. We have to change the function so that it always ensures |