X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gold%2Fx86_64.cc;h=9110278ebb8414ef2dcccc822734202aad21a320;hb=ce97fa81e0c46d216b80b143ad8c02fff6906fef;hp=a7e7a39224d60e5934f35548b8acd19cb46039d0;hpb=e785ec03fb73baeb3699bb46901b2c04a415fd58;p=deliverable%2Fbinutils-gdb.git diff --git a/gold/x86_64.cc b/gold/x86_64.cc index a7e7a39224..9110278ebb 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -39,6 +39,7 @@ #include "tls.h" #include "freebsd.h" #include "gc.h" +#include "icf.h" namespace { @@ -65,9 +66,20 @@ 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) { } + // This function should be defined in targets that can use relocation + // types to determine (implemented in local_reloc_may_be_function_pointer + // and global_reloc_may_be_function_pointer) + // if a function's pointer is taken. ICF uses this in safe mode to only + // fold those functions whose pointer is defintely not taken. For x86_64 + // pie binaries, safe ICF cannot be done by looking at relocation types. + inline bool + can_check_for_function_pointers() const + { return !parameters->options().pie(); } + // Hook for a new output section. void do_new_output_section(Output_section*) const; @@ -161,6 +173,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 +202,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 @@ -201,6 +235,26 @@ class Target_x86_64 : public Target_freebsd<64, false> const elfcpp::Rela<64, false>& reloc, unsigned int r_type, Symbol* gsym); + inline bool + local_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout, + Target_x86_64* target, + Sized_relobj<64, false>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rela<64, false>& reloc, + unsigned int r_type, + const elfcpp::Sym<64, false>& lsym); + + inline bool + global_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout, + Target_x86_64* target, + Sized_relobj<64, false>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rela<64, false>& reloc, + unsigned int r_type, + Symbol* gsym); + private: static void unsupported_reloc_local(Sized_relobj<64, false>*, unsigned int r_type); @@ -212,6 +266,9 @@ class Target_x86_64 : public Target_freebsd<64, false> void check_non_pic(Relobj*, unsigned int r_type); + inline bool + possible_function_pointer_reloc(unsigned int r_type); + // Whether we have issued an error about a non-PIC compilation. bool issued_non_pic_error_; }; @@ -379,6 +436,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 +465,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 +496,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_reloc_info_; // True if the _TLS_MODULE_BASE_ symbol has been defined. bool tls_base_symbol_defined_; }; @@ -551,11 +631,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 +674,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 +694,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 +739,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 +918,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 +1312,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(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); @@ -1282,6 +1398,77 @@ Target_x86_64::Scan::unsupported_reloc_global(Sized_relobj<64, false>* object, object->name().c_str(), r_type, gsym->demangled_name().c_str()); } +// Returns true if this relocation type could be that of a function pointer. +inline bool +Target_x86_64::Scan::possible_function_pointer_reloc(unsigned int r_type) +{ + switch (r_type) + { + case elfcpp::R_X86_64_64: + case elfcpp::R_X86_64_32: + case elfcpp::R_X86_64_32S: + case elfcpp::R_X86_64_16: + case elfcpp::R_X86_64_8: + case elfcpp::R_X86_64_GOT64: + case elfcpp::R_X86_64_GOT32: + case elfcpp::R_X86_64_GOTPCREL64: + case elfcpp::R_X86_64_GOTPCREL: + case elfcpp::R_X86_64_GOTPLT64: + { + return true; + } + } + return false; +} + +// For safe ICF, scan a relocation for a local symbol to check if it +// corresponds to a function pointer being taken. In that case mark +// the function whose pointer was taken as not foldable. + +inline bool +Target_x86_64::Scan::local_reloc_may_be_function_pointer( + Symbol_table* , + Layout* , + Target_x86_64* , + Sized_relobj<64, false>* , + unsigned int , + Output_section* , + const elfcpp::Rela<64, false>& , + unsigned int r_type, + const elfcpp::Sym<64, false>&) +{ + // When building a shared library, do not fold any local symbols as it is + // not possible to distinguish pointer taken versus a call by looking at + // the relocation types. + return (parameters->options().shared() + || possible_function_pointer_reloc(r_type)); +} + +// For safe ICF, scan a relocation for a global symbol to check if it +// corresponds to a function pointer being taken. In that case mark +// the function whose pointer was taken as not foldable. + +inline bool +Target_x86_64::Scan::global_reloc_may_be_function_pointer( + Symbol_table*, + Layout* , + Target_x86_64* , + Sized_relobj<64, false>* , + unsigned int , + Output_section* , + const elfcpp::Rela<64, false>& , + unsigned int r_type, + Symbol* gsym) +{ + // When building a shared library, do not fold symbols whose visibility + // is hidden, internal or protected. + return ((parameters->options().shared() + && (gsym->visibility() == elfcpp::STV_INTERNAL + || gsym->visibility() == elfcpp::STV_PROTECTED + || gsym->visibility() == elfcpp::STV_HIDDEN)) + || possible_function_pointer_reloc(r_type)); +} + // Scan a relocation for a global symbol. inline void @@ -1505,8 +1692,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) @@ -1655,48 +1842,27 @@ Target_x86_64::do_finalize_sections( const Input_objects*, Symbol_table* symtab) { + const Reloc_section* rel_plt = (this->plt_ == NULL + ? NULL + : this->plt_->rela_plt()); + layout->add_target_dynamic_tags(false, this->got_plt_, rel_plt, + this->rela_dyn_, true, false); + // Fill in some more dynamic tags. Output_data_dynamic* const odyn = layout->dynamic_data(); if (odyn != NULL) { - if (this->got_plt_ != NULL - && this->got_plt_->output_section() != NULL) - odyn->add_section_address(elfcpp::DT_PLTGOT, this->got_plt_); - if (this->plt_ != NULL - && this->plt_->output_section() != NULL) - { - const Output_data* od = this->plt_->rel_plt(); - odyn->add_section_size(elfcpp::DT_PLTRELSZ, od); - odyn->add_section_address(elfcpp::DT_JMPREL, od); - odyn->add_constant(elfcpp::DT_PLTREL, elfcpp::DT_RELA); - if (this->plt_->has_tlsdesc_entry()) - { - unsigned int plt_offset = this->plt_->get_tlsdesc_plt_offset(); - unsigned int got_offset = this->plt_->get_tlsdesc_got_offset(); - this->got_->finalize_data_size(); - odyn->add_section_plus_offset(elfcpp::DT_TLSDESC_PLT, - this->plt_, plt_offset); - odyn->add_section_plus_offset(elfcpp::DT_TLSDESC_GOT, - this->got_, got_offset); - } - } - - if (this->rela_dyn_ != NULL - && this->rela_dyn_->output_section() != NULL) - { - const Output_data* od = this->rela_dyn_; - odyn->add_section_address(elfcpp::DT_RELA, od); - odyn->add_section_size(elfcpp::DT_RELASZ, od); - odyn->add_constant(elfcpp::DT_RELAENT, - elfcpp::Elf_sizes<64>::rela_size); - } - - if (!parameters->options().shared()) + && this->plt_->output_section() != NULL + && this->plt_->has_tlsdesc_entry()) { - // The value of the DT_DEBUG tag is filled in by the dynamic - // linker at run time, and used by the debugger. - odyn->add_constant(elfcpp::DT_DEBUG, 0); + unsigned int plt_offset = this->plt_->get_tlsdesc_plt_offset(); + unsigned int got_offset = this->plt_->get_tlsdesc_got_offset(); + this->got_->finalize_data_size(); + odyn->add_section_plus_offset(elfcpp::DT_TLSDESC_PLT, + this->plt_, plt_offset); + odyn->add_section_plus_offset(elfcpp::DT_TLSDESC_GOT, + this->got_, got_offset); } } @@ -1992,7 +2158,7 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, elfcpp::Elf_types<64>::Elf_Addr value = psymval->value(relinfo->object, 0); const bool is_final = (gsym == NULL - ? !parameters->options().output_is_position_independent() + ? !parameters->options().shared() : gsym->final_value_is_known()); const tls::Tls_optimization optimized_type = Target_x86_64::optimize_tls_reloc(is_final, r_type); @@ -2679,6 +2845,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(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