X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gold%2Fx86_64.cc;h=e61d7d1bf590f8e30ea8ff07f1427a173a10bfc7;hb=bb593acb76bc52b23ddbad3f9b5199be26879da5;hp=d67069e7010779fc657f30153df5bd4846a78520;hpb=e6455dfbc2dae8cc91f64b4120e87e138970cf84;p=deliverable%2Fbinutils-gdb.git diff --git a/gold/x86_64.cc b/gold/x86_64.cc index d67069e701..e61d7d1bf5 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -1,6 +1,6 @@ // x86_64.cc -- x86_64 target support for gold. -// Copyright 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +// Copyright 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. @@ -25,6 +25,7 @@ #include #include "elfcpp.h" +#include "dwarf.h" #include "parameters.h" #include "reloc.h" #include "x86_64.h" @@ -53,22 +54,25 @@ class Output_data_plt_x86_64 : public Output_section_data public: typedef Output_data_reloc Reloc_section; - Output_data_plt_x86_64(Symbol_table* symtab, Layout* layout, - Output_data_got<64, false>* got, - Output_data_space* got_plt) - : Output_section_data(8), tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), - count_(0), tlsdesc_got_offset_(-1U), free_list_() - { this->init(symtab, layout); } - - Output_data_plt_x86_64(Symbol_table* symtab, Layout* layout, - Output_data_got<64, false>* got, + Output_data_plt_x86_64(Layout* layout, Output_data_got<64, false>* got, + Output_data_space* got_plt, + Output_data_space* got_irelative) + : Output_section_data(16), layout_(layout), tlsdesc_rel_(NULL), + irelative_rel_(NULL), got_(got), got_plt_(got_plt), + got_irelative_(got_irelative), count_(0), irelative_count_(0), + tlsdesc_got_offset_(-1U), free_list_() + { this->init(layout); } + + Output_data_plt_x86_64(Layout* layout, Output_data_got<64, false>* got, Output_data_space* got_plt, + Output_data_space* got_irelative, unsigned int plt_count) - : Output_section_data((plt_count + 1) * plt_entry_size, 8, false), - tlsdesc_rel_(NULL), got_(got), got_plt_(got_plt), - count_(plt_count), tlsdesc_got_offset_(-1U), free_list_() + : Output_section_data((plt_count + 1) * plt_entry_size, 16, false), + layout_(layout), tlsdesc_rel_(NULL), irelative_rel_(NULL), got_(got), + got_plt_(got_plt), got_irelative_(got_irelative), count_(plt_count), + irelative_count_(0), tlsdesc_got_offset_(-1U), free_list_() { - this->init(symtab, layout); + this->init(layout); // Initialize the free list and reserve the first entry. this->free_list_.init((plt_count + 1) * plt_entry_size, false); @@ -77,20 +81,22 @@ class Output_data_plt_x86_64 : public Output_section_data // Initialize the PLT section. void - init(Symbol_table* symtab, Layout* layout); + init(Layout* layout); // 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<64, false>* relobj, + add_local_ifunc_entry(Symbol_table* symtab, Layout*, + Sized_relobj_file<64, false>* relobj, unsigned int local_sym_index); // Add the relocation for a PLT entry. void - add_relocation(Symbol* gsym, unsigned int got_offset); + add_relocation(Symbol_table*, Layout*, Symbol* gsym, + unsigned int got_offset); // Add the reserved TLSDESC_PLT entry to the PLT. void @@ -110,7 +116,7 @@ class Output_data_plt_x86_64 : public Output_section_data // Return the offset of the reserved TLSDESC_PLT entry. unsigned int get_tlsdesc_plt_offset() const - { return (this->count_ + 1) * plt_entry_size; } + { return (this->count_ + this->irelative_count_ + 1) * plt_entry_size; } // Return the .rela.plt section data. Reloc_section* @@ -121,10 +127,20 @@ class Output_data_plt_x86_64 : public Output_section_data Reloc_section* rela_tlsdesc(Layout*); + // Return where the IRELATIVE relocations should go in the PLT + // relocations. + Reloc_section* + rela_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 @@ -144,6 +160,14 @@ class Output_data_plt_x86_64 : public Output_section_data (plt_index + 2) * plt_entry_size); } + // 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); + protected: void do_adjust_output_section(Output_section* os); @@ -160,13 +184,19 @@ class Output_data_plt_x86_64 : public Output_section_data // The first entry in the PLT. // From the AMD64 ABI: "Unlike Intel386 ABI, this ABI uses the same // procedure linkage table for both programs and shared objects." - static unsigned char first_plt_entry[plt_entry_size]; + static const unsigned char first_plt_entry[plt_entry_size]; // Other entries in the PLT for an executable. - static unsigned char plt_entry[plt_entry_size]; + static const unsigned char plt_entry[plt_entry_size]; // The reserved TLSDESC entry in the PLT for an executable. - static unsigned char tlsdesc_plt_entry[plt_entry_size]; + static const unsigned char tlsdesc_plt_entry[plt_entry_size]; + + // The .eh_frame unwind information for the PLT. + 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]; // Set the final size. void @@ -176,17 +206,28 @@ class Output_data_plt_x86_64 : public Output_section_data void do_write(Output_file*); + // A pointer to the Layout class, so that we can find the .dynamic + // section when we write out the GOT PLT section. + Layout* layout_; // The reloc section. Reloc_section* rel_; // The TLSDESC relocs, if necessary. These must follow the regular // PLT relocs. Reloc_section* tlsdesc_rel_; + // The IRELATIVE relocs, if necessary. These must follow the + // regular PLT relocations and the TLSDESC relocations. + Reloc_section* irelative_rel_; // The .got section. Output_data_got<64, false>* got_; // The .got.plt section. Output_data_space* 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_X86_64_IRELATIVE relocs. These + // follow the regular PLT entries. + unsigned int irelative_count_; // Offset of the reserved TLSDESC_GOT entry when needed. unsigned int tlsdesc_got_offset_; // List of available regions within the section, for incremental @@ -201,7 +242,7 @@ class Output_data_plt_x86_64 : public Output_section_data // http://people.redhat.com/drepper/tls.pdf // http://www.lsd.ic.unicamp.br/~oliva/writeups/TLS/RFC-TLSDESC-x86.txt -class Target_x86_64 : public Target_freebsd<64, false> +class Target_x86_64 : public Sized_target<64, false> { public: // In the x86_64 ABI (p 68), it says "The AMD64 ABI architectures @@ -209,28 +250,14 @@ class Target_x86_64 : public Target_freebsd<64, false> typedef Output_data_reloc Reloc_section; Target_x86_64() - : Target_freebsd<64, false>(&x86_64_info), - got_(NULL), plt_(NULL), got_plt_(NULL), got_tlsdesc_(NULL), - global_offset_table_(NULL), rela_dyn_(NULL), - copy_relocs_(elfcpp::R_X86_64_COPY), dynbss_(NULL), - got_mod_index_offset_(-1U), tlsdesc_reloc_info_(), + : Sized_target<64, false>(&x86_64_info), + got_(NULL), plt_(NULL), got_plt_(NULL), got_irelative_(NULL), + got_tlsdesc_(NULL), global_offset_table_(NULL), rela_dyn_(NULL), + rela_irelative_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY), + dynbss_(NULL), 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(); } - - virtual bool - can_icf_inline_merge_sections () const - { return true; } - // Hook for a new output section. void do_new_output_section(Output_section*) const; @@ -339,13 +366,27 @@ class Target_x86_64 : public Target_freebsd<64, false> do_reloc_addend(void* arg, unsigned int r_type, uint64_t addend) const; // Return the PLT section. - Output_data* - do_plt_section_for_global(const Symbol*) const - { return this->plt_section(); } + uint64_t + do_plt_address_for_global(const Symbol* gsym) const + { return this->plt_section()->address_for_global(gsym); } + + uint64_t + do_plt_address_for_local(const Relobj* relobj, unsigned int symndx) const + { return this->plt_section()->address_for_local(relobj, symndx); } + + // 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. + bool + do_can_check_for_function_pointers() const + { return !parameters->options().pie(); } - Output_data* - do_plt_section_for_local(const Relobj*, unsigned int) const - { return this->plt_section(); } + // Return the base for a DW_EH_PE_datarel encoding. + uint64_t + do_ehframe_datarel_base() const; // Adjust -fsplit-stack code which calls non-split-stack code. void @@ -406,7 +447,8 @@ class Target_x86_64 : public Target_freebsd<64, false> // Register an existing PLT entry for a global symbol. void - register_global_plt_entry(unsigned int plt_index, Symbol* gsym); + register_global_plt_entry(Symbol_table*, Layout*, unsigned int plt_index, + Symbol* gsym); // Force a COPY relocation for a given symbol. void @@ -488,7 +530,7 @@ class Target_x86_64 : public Target_freebsd<64, false> Symbol*); void - check_non_pic(Relobj*, unsigned int r_type); + check_non_pic(Relobj*, unsigned int r_type, Symbol*); inline bool possible_function_pointer_reloc(unsigned int r_type); @@ -676,6 +718,10 @@ class Target_x86_64 : public Target_freebsd<64, false> Reloc_section* rela_tlsdesc_section(Layout*) const; + // Get the section to use for IRELATIVE relocations. + Reloc_section* + rela_irelative_section(Layout*); + // Add a potential copy relocation. void copy_reloc(Symbol_table* symtab, Layout* layout, @@ -726,12 +772,16 @@ class Target_x86_64 : public Target_freebsd<64, false> Output_data_plt_x86_64* plt_; // The GOT PLT section. Output_data_space* got_plt_; + // The GOT section for IRELATIVE relocations. + Output_data_space* got_irelative_; // The GOT section for TLSDESC relocations. Output_data_got<64, false>* got_tlsdesc_; // The _GLOBAL_OFFSET_TABLE_ symbol. Symbol* global_offset_table_; // The dynamic reloc section. Reloc_section* rela_dyn_; + // The section to use for IRELATIVE relocs. + Reloc_section* rela_irelative_; // Relocs saved to avoid a COPY reloc. Copy_relocs copy_relocs_; // Space for variables copied with a COPY reloc. @@ -755,6 +805,7 @@ const Target::Target_info Target_x86_64::x86_64_info = false, // has_resolve true, // has_code_fill true, // is_default_stack_executable + true, // can_icf_inline_merge_sections '\0', // wrap_char "/lib/ld64.so.1", // program interpreter 0x400000, // default_text_segment_address @@ -787,26 +838,39 @@ Target_x86_64::got_section(Symbol_table* symtab, Layout* layout) { gold_assert(symtab != NULL && layout != NULL); + // 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); + this->got_ = new Output_data_got<64, false>(); 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(8, "** GOT PLT"); 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 * 8); - // Those bytes can go into the relro segment. - layout->increase_relro(3 * 8); + if (!is_got_plt_relro) + { + // Those bytes can go into the relro segment. + layout->increase_relro(3 * 8); + } // Define _GLOBAL_OFFSET_TABLE_ at the start of the PLT. this->global_offset_table_ = @@ -818,14 +882,23 @@ Target_x86_64::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 entries. + this->got_irelative_ = new Output_data_space(8, "** 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. + // .got.plt after the jump slot and IRELATIVE entries. this->got_tlsdesc_ = new Output_data_got<64, false>(); layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE), this->got_tlsdesc_, - ORDER_NON_RELRO_FIRST, false); + got_plt_order, is_got_plt_relro); } return this->got_; @@ -847,33 +920,43 @@ Target_x86_64::rela_dyn_section(Layout* layout) return this->rela_dyn_; } +// Get the section to use for IRELATIVE relocs, creating it if +// necessary. These go in .rela.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_x86_64::Reloc_section* +Target_x86_64::rela_irelative_section(Layout* layout) +{ + if (this->rela_irelative_ == NULL) + { + // Make sure we have already created the dynamic reloc section. + this->rela_dyn_section(layout); + this->rela_irelative_ = new Reloc_section(false); + layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA, + elfcpp::SHF_ALLOC, this->rela_irelative_, + ORDER_DYNAMIC_RELOCS, false); + gold_assert(this->rela_dyn_->output_section() + == this->rela_irelative_->output_section()); + } + return this->rela_irelative_; +} + // Initialize the PLT section. void -Output_data_plt_x86_64::init(Symbol_table* symtab, Layout* layout) +Output_data_plt_x86_64::init(Layout* layout) { this->rel_ = new Reloc_section(false); layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, elfcpp::SHF_ALLOC, this->rel_, ORDER_DYNAMIC_PLT_RELOCS, false); - if (parameters->doing_static_link()) - { - // A statically linked executable will only have a .rela.plt - // section to hold R_X86_64_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("__rela_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("__rela_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 @@ -885,7 +968,8 @@ Output_data_plt_x86_64::do_adjust_output_section(Output_section* os) // Add an entry to the PLT. void -Output_data_plt_x86_64::add_entry(Symbol* gsym) +Output_data_plt_x86_64::add_entry(Symbol_table* symtab, Layout* layout, + Symbol* gsym) { gold_assert(!gsym->has_plt_offset()); @@ -893,25 +977,47 @@ Output_data_plt_x86_64::add_entry(Symbol* gsym) off_t plt_offset; section_offset_type got_offset; + unsigned int* pcount; + unsigned int offset; + unsigned int reserved; + Output_data_space* got; + if (gsym->type() == elfcpp::STT_GNU_IFUNC + && gsym->can_use_relative_reloc(false)) + { + pcount = &this->irelative_count_; + offset = 0; + reserved = 0; + got = this->got_irelative_; + } + else + { + pcount = &this->count_; + offset = 1; + reserved = 3; + got = this->got_plt_; + } + if (!this->is_data_size_valid()) { - // Note that when setting the PLT offset we skip the initial - // reserved PLT entry. - plt_index = this->count_ + 1; + // Note that when setting the PLT offset for a non-IRELATIVE + // entry we skip the initial reserved PLT entry. + plt_index = *pcount + offset; plt_offset = plt_index * plt_entry_size; - ++this->count_; + ++*pcount; - got_offset = (plt_index - 1 + 3) * 8; - gold_assert(got_offset == this->got_plt_->current_data_size()); + got_offset = (plt_index - offset + reserved) * 8; + gold_assert(got_offset == got->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 + 8); + got->set_current_data_size(got_offset + 8); } else { + // FIXME: This is probably not correct for IRELATIVE relocs. + // For incremental updates, find an available slot. plt_offset = this->free_list_.allocate(plt_entry_size, plt_entry_size, 0); if (plt_offset == -1) @@ -922,13 +1028,13 @@ Output_data_plt_x86_64::add_entry(Symbol* gsym) // can be calculated from the PLT index, adjusting for the three // reserved entries at the beginning of the GOT. plt_index = plt_offset / plt_entry_size - 1; - got_offset = (plt_index - 1 + 3) * 8; + got_offset = (plt_index - offset + reserved) * 8; } gsym->set_plt_offset(plt_offset); // Every PLT entry needs a reloc. - this->add_relocation(gsym, got_offset); + this->add_relocation(symtab, layout, gsym, got_offset); // Note that we don't need to save the symbol. The contents of the // PLT are independent of which symbols are used. The symbols only @@ -940,22 +1046,25 @@ Output_data_plt_x86_64::add_entry(Symbol* gsym) unsigned int Output_data_plt_x86_64::add_local_ifunc_entry( + Symbol_table* symtab, + Layout* layout, Sized_relobj_file<64, 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_ * 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 + 8); + this->got_irelative_->set_current_data_size(got_offset + 8); // Every PLT entry needs a reloc. - this->rel_->add_symbolless_local_addend(relobj, local_sym_index, - elfcpp::R_X86_64_IRELATIVE, - this->got_plt_, got_offset, 0); + Reloc_section* rela = this->rela_irelative(symtab, layout); + rela->add_symbolless_local_addend(relobj, local_sym_index, + elfcpp::R_X86_64_IRELATIVE, + this->got_irelative_, got_offset, 0); return plt_offset; } @@ -963,12 +1072,16 @@ Output_data_plt_x86_64::add_local_ifunc_entry( // Add the relocation for a PLT entry. void -Output_data_plt_x86_64::add_relocation(Symbol* gsym, unsigned int got_offset) +Output_data_plt_x86_64::add_relocation(Symbol_table* symtab, Layout* layout, + Symbol* gsym, unsigned int got_offset) { if (gsym->type() == elfcpp::STT_GNU_IFUNC && gsym->can_use_relative_reloc(false)) - this->rel_->add_symbolless_global_addend(gsym, elfcpp::R_X86_64_IRELATIVE, - this->got_plt_, got_offset, 0); + { + Reloc_section* rela = this->rela_irelative(symtab, layout); + rela->add_symbolless_global_addend(gsym, elfcpp::R_X86_64_IRELATIVE, + this->got_irelative_, got_offset, 0); + } else { gsym->set_needs_dynsym_entry(); @@ -989,17 +1102,78 @@ Output_data_plt_x86_64::rela_tlsdesc(Layout* layout) layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, elfcpp::SHF_ALLOC, this->tlsdesc_rel_, ORDER_DYNAMIC_PLT_RELOCS, false); - gold_assert(this->tlsdesc_rel_->output_section() == - this->rel_->output_section()); + gold_assert(this->tlsdesc_rel_->output_section() + == this->rel_->output_section()); } return this->tlsdesc_rel_; } +// Return where the IRELATIVE relocations should go in the PLT. These +// follow the JUMP_SLOT and the TLSDESC relocations. + +Output_data_plt_x86_64::Reloc_section* +Output_data_plt_x86_64::rela_irelative(Symbol_table* symtab, Layout* layout) +{ + if (this->irelative_rel_ == NULL) + { + // Make sure we have a place for the TLSDESC relocations, in + // case we see any later on. + this->rela_tlsdesc(layout); + this->irelative_rel_ = new Reloc_section(false); + layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA, + 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 .rela.plt + // section to hold R_X86_64_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("__rela_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("__rela_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_x86_64::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) * plt_entry_size; + return this->address() + offset; +} + +// Return the PLT address to use for a local symbol. These are always +// IRELATIVE relocs. + +uint64_t +Output_data_plt_x86_64::address_for_local(const Relobj*, unsigned int) +{ + return this->address() + (this->count_ + 1) * plt_entry_size; +} + // Set the final size. void Output_data_plt_x86_64::set_final_data_size() { - unsigned int count = this->count_; + unsigned int count = this->count_ + this->irelative_count_; if (this->has_tlsdesc_entry()) ++count; this->set_data_size((count + 1) * plt_entry_size); @@ -1007,7 +1181,7 @@ Output_data_plt_x86_64::set_final_data_size() // The first entry in the PLT for an executable. -unsigned char Output_data_plt_x86_64::first_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_x86_64::first_plt_entry[plt_entry_size] = { // From AMD64 ABI Draft 0.98, page 76 0xff, 0x35, // pushq contents of memory address @@ -1019,7 +1193,7 @@ unsigned char Output_data_plt_x86_64::first_plt_entry[plt_entry_size] = // Subsequent entries in the PLT for an executable. -unsigned char Output_data_plt_x86_64::plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_x86_64::plt_entry[plt_entry_size] = { // From AMD64 ABI Draft 0.98, page 76 0xff, 0x25, // jmpq indirect @@ -1032,7 +1206,7 @@ unsigned char Output_data_plt_x86_64::plt_entry[plt_entry_size] = // The reserved TLSDESC entry in the PLT for an executable. -unsigned char Output_data_plt_x86_64::tlsdesc_plt_entry[plt_entry_size] = +const unsigned char Output_data_plt_x86_64::tlsdesc_plt_entry[plt_entry_size] = { // From Alexandre Oliva, "Thread-Local Storage Descriptors for IA32 // and AMD64/EM64T", Version 0.9.4 (2005-10-10). @@ -1044,6 +1218,54 @@ unsigned char Output_data_plt_x86_64::tlsdesc_plt_entry[plt_entry_size] = 0x40, 0 }; +// The .eh_frame unwind information for the PLT. + +const unsigned char +Output_data_plt_x86_64::plt_eh_frame_cie[plt_eh_frame_cie_size] = +{ + 1, // CIE version. + 'z', // Augmentation: augmentation size included. + 'R', // Augmentation: FDE encoding included. + '\0', // End of augmentation string. + 1, // Code alignment factor. + 0x78, // Data alignment factor. + 16, // Return address column. + 1, // Augmentation size. + (elfcpp::DW_EH_PE_pcrel // FDE encoding. + | elfcpp::DW_EH_PE_sdata4), + elfcpp::DW_CFA_def_cfa, 7, 8, // DW_CFA_def_cfa: r7 (rsp) ofs 8. + elfcpp::DW_CFA_offset + 16, 1,// DW_CFA_offset: r16 (rip) at cfa-8. + elfcpp::DW_CFA_nop, // Align to 16 bytes. + elfcpp::DW_CFA_nop +}; + +const unsigned char +Output_data_plt_x86_64::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, 16, // DW_CFA_def_cfa_offset: 16. + elfcpp::DW_CFA_advance_loc + 6, // Advance 6 to __PLT__ + 6. + elfcpp::DW_CFA_def_cfa_offset, 24, // DW_CFA_def_cfa_offset: 24. + elfcpp::DW_CFA_advance_loc + 10, // Advance 10 to __PLT__ + 16. + elfcpp::DW_CFA_def_cfa_expression, // DW_CFA_def_cfa_expression. + 11, // Block length. + elfcpp::DW_OP_breg7, 8, // Push %rsp + 8. + elfcpp::DW_OP_breg16, 0, // Push %rip. + elfcpp::DW_OP_lit15, // Push 0xf. + elfcpp::DW_OP_and, // & (%rip & 0xf). + elfcpp::DW_OP_lit11, // Push 0xb. + elfcpp::DW_OP_ge, // >= ((%rip & 0xf) >= 0xb) + elfcpp::DW_OP_lit3, // Push 3. + elfcpp::DW_OP_shl, // << (((%rip & 0xf) >= 0xb) << 3) + elfcpp::DW_OP_plus, // + ((((%rip&0xf)>=0xb)<<3)+%rsp+8 + elfcpp::DW_CFA_nop, // Align to 32 bytes. + elfcpp::DW_CFA_nop, + elfcpp::DW_CFA_nop, + elfcpp::DW_CFA_nop +}; + // Write out the PLT. This uses the hand-coded instructions above, // and adjusts them as needed. This is specified by the AMD64 ABI. @@ -1056,8 +1278,12 @@ Output_data_plt_x86_64::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); @@ -1084,12 +1310,20 @@ Output_data_plt_x86_64::do_write(Output_file* of) unsigned char* got_pov = got_view; - memset(got_pov, 0, 24); - got_pov += 24; + // 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_x86_64::got_section. + Output_section* dynamic = this->layout_->dynamic_section(); + uint32_t dynamic_addr = dynamic == NULL ? 0 : dynamic->address(); + elfcpp::Swap<64, false>::writeval(got_pov, dynamic_addr); + got_pov += 8; + memset(got_pov, 0, 16); + got_pov += 16; unsigned int plt_offset = plt_entry_size; unsigned int got_offset = 24; - const unsigned int count = this->count_; + const unsigned int count = this->count_ + this->irelative_count_; for (unsigned int plt_index = 0; plt_index < count; ++plt_index, @@ -1147,8 +1381,9 @@ Target_x86_64::make_plt_section(Symbol_table* symtab, Layout* layout) // Create the GOT sections first. this->got_section(symtab, layout); - this->plt_ = new Output_data_plt_x86_64(symtab, layout, this->got_, - this->got_plt_); + this->plt_ = new Output_data_plt_x86_64(layout, this->got_, + this->got_plt_, + this->got_irelative_); layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR), @@ -1180,7 +1415,7 @@ Target_x86_64::make_plt_entry(Symbol_table* symtab, Layout* layout, 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. @@ -1194,7 +1429,8 @@ Target_x86_64::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); } @@ -1269,9 +1505,17 @@ Target_x86_64::init_got_plt_for_update(Symbol_table* symtab, this->got_tlsdesc_, ORDER_NON_RELRO_FIRST, false); + // If there are any IRELATIVE relocations, they get GOT entries in + // .got.plt after the jump slot and TLSDESC entries. + this->got_irelative_ = new Output_data_space(0, 8, "** GOT IRELATIVE PLT"); + layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, + this->got_irelative_, + ORDER_NON_RELRO_FIRST, false); + // Create the PLT section. - this->plt_ = new Output_data_plt_x86_64(symtab, layout, this->got_, - this->got_plt_, plt_count); + this->plt_ = new Output_data_plt_x86_64(layout, this->got_, this->got_plt_, + this->got_irelative_, plt_count); layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR, this->plt_, ORDER_PLT, false); @@ -1305,7 +1549,7 @@ Target_x86_64::reserve_local_got_entry( case GOT_TYPE_STANDARD: if (parameters->options().output_is_position_independent()) rela_dyn->add_local_relative(obj, r_sym, elfcpp::R_X86_64_RELATIVE, - this->got_, got_offset, 0); + this->got_, got_offset, 0, false); break; case GOT_TYPE_TLS_OFFSET: rela_dyn->add_local(obj, r_sym, elfcpp::R_X86_64_TPOFF64, @@ -1378,7 +1622,9 @@ Target_x86_64::reserve_global_got_entry(unsigned int got_index, Symbol* gsym, // Register an existing PLT entry for a global symbol. void -Target_x86_64::register_global_plt_entry(unsigned int plt_index, +Target_x86_64::register_global_plt_entry(Symbol_table* symtab, + Layout* layout, + unsigned int plt_index, Symbol* gsym) { gold_assert(this->plt_ != NULL); @@ -1389,7 +1635,7 @@ Target_x86_64::register_global_plt_entry(unsigned int plt_index, gsym->set_plt_offset((plt_index + 1) * this->plt_entry_size()); unsigned int got_offset = (plt_index + 3) * 8; - this->plt_->add_relocation(gsym, got_offset); + this->plt_->add_relocation(symtab, layout, gsym, got_offset); } // Force a COPY relocation for a given symbol. @@ -1610,10 +1856,13 @@ Target_x86_64::Scan::unsupported_reloc_local( // Here we know the section is allocated, but we don't know that it is // read-only. But we check for all the relocation types which the // glibc dynamic linker supports, so it seems appropriate to issue an -// error even if the section is not read-only. +// error even if the section is not read-only. If GSYM is not NULL, +// it is the symbol the relocation is against; if it is NULL, the +// relocation is against a local symbol. void -Target_x86_64::Scan::check_non_pic(Relobj* object, unsigned int r_type) +Target_x86_64::Scan::check_non_pic(Relobj* object, unsigned int r_type, + Symbol* gsym) { switch (r_type) { @@ -1631,13 +1880,29 @@ Target_x86_64::Scan::check_non_pic(Relobj* object, unsigned int r_type) return; // glibc supports these reloc types, but they can overflow. - case elfcpp::R_X86_64_32: case elfcpp::R_X86_64_PC32: + // A PC relative reference is OK against a local symbol or if + // the symbol is defined locally. + if (gsym == NULL + || (!gsym->is_from_dynobj() + && !gsym->is_undefined() + && !gsym->is_preemptible())) + return; + /* Fall through. */ + case elfcpp::R_X86_64_32: if (this->issued_non_pic_error_) return; gold_assert(parameters->options().output_is_position_independent()); - object->error(_("requires dynamic reloc which may overflow at runtime; " - "recompile with -fPIC")); + if (gsym == NULL) + object->error(_("requires dynamic R_X86_64_32 reloc which may " + "overflow at runtime; recompile with -fPIC")); + else + object->error(_("requires dynamic %s reloc against '%s' which may " + "overflow at runtime; recompile with -fPIC"), + (r_type == elfcpp::R_X86_64_32 + ? "R_X86_64_32" + : "R_X86_64_PC32"), + gsym->name()); this->issued_non_pic_error_ = true; return; @@ -1648,8 +1913,9 @@ Target_x86_64::Scan::check_non_pic(Relobj* object, unsigned int r_type) if (this->issued_non_pic_error_) return; gold_assert(parameters->options().output_is_position_independent()); - object->error(_("requires unsupported dynamic reloc; " - "recompile with -fPIC")); + object->error(_("requires unsupported dynamic reloc %u; " + "recompile with -fPIC"), + r_type); this->issued_non_pic_error_ = true; return; @@ -1687,8 +1953,8 @@ Target_x86_64::Scan::local(Symbol_table* symtab, const elfcpp::Sym<64, false>& lsym) { // 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)) + bool is_ifunc = lsym.get_st_type() == elfcpp::STT_GNU_IFUNC; + if (is_ifunc && this->reloc_needs_plt_for_ifunc(object, r_type)) { unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info()); target->make_local_ifunc_plt_entry(symtab, layout, object, r_sym); @@ -1716,7 +1982,7 @@ Target_x86_64::Scan::local(Symbol_table* symtab, elfcpp::R_X86_64_RELATIVE, output_section, data_shndx, reloc.get_r_offset(), - reloc.get_r_addend()); + reloc.get_r_addend(), is_ifunc); } break; @@ -1730,7 +1996,7 @@ Target_x86_64::Scan::local(Symbol_table* symtab, // because that is always a 64-bit relocation. if (parameters->options().output_is_position_independent()) { - this->check_non_pic(object, r_type); + this->check_non_pic(object, r_type, NULL); Reloc_section* rela_dyn = target->rela_dyn_section(layout); unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info()); @@ -1792,7 +2058,7 @@ Target_x86_64::Scan::local(Symbol_table* symtab, // lets function pointers compare correctly with shared // libraries. Otherwise we would need an IRELATIVE reloc. bool is_new; - if (lsym.get_st_type() == elfcpp::STT_GNU_IFUNC) + if (is_ifunc) is_new = got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD); else is_new = got->add_local(object, r_sym, GOT_TYPE_STANDARD); @@ -1810,11 +2076,11 @@ Target_x86_64::Scan::local(Symbol_table* symtab, object->local_got_offset(r_sym, GOT_TYPE_STANDARD); rela_dyn->add_local_relative(object, r_sym, elfcpp::R_X86_64_RELATIVE, - got, got_offset, 0); + got, got_offset, 0, is_ifunc); } else { - this->check_non_pic(object, r_type); + this->check_non_pic(object, r_type, NULL); gold_assert(lsym.get_st_type() != elfcpp::STT_SECTION); rela_dyn->add_local( @@ -2115,7 +2381,8 @@ Target_x86_64::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* rela_dyn = target->rela_dyn_section(layout); + Reloc_section* rela_dyn = + target->rela_irelative_section(layout); unsigned int r_type = elfcpp::R_X86_64_IRELATIVE; rela_dyn->add_symbolless_global_addend(gsym, r_type, output_section, object, @@ -2135,7 +2402,7 @@ Target_x86_64::Scan::global(Symbol_table* symtab, } else { - this->check_non_pic(object, r_type); + this->check_non_pic(object, r_type, gsym); Reloc_section* rela_dyn = target->rela_dyn_section(layout); rela_dyn->add_global(gsym, r_type, output_section, object, data_shndx, reloc.get_r_offset(), @@ -2163,7 +2430,7 @@ Target_x86_64::Scan::global(Symbol_table* symtab, } else { - this->check_non_pic(object, r_type); + this->check_non_pic(object, r_type, gsym); Reloc_section* rela_dyn = target->rela_dyn_section(layout); rela_dyn->add_global(gsym, r_type, output_section, object, data_shndx, reloc.get_r_offset(), @@ -2194,9 +2461,24 @@ Target_x86_64::Scan::global(Symbol_table* symtab, // If this symbol is not fully resolved, we need to add a // dynamic relocation for it. Reloc_section* rela_dyn = target->rela_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_rela(gsym, GOT_TYPE_STANDARD, rela_dyn, @@ -2518,6 +2800,47 @@ Target_x86_64::do_finalize_sections( uint64_t data_size = this->got_plt_->current_data_size(); symtab->get_sized_symbol<64>(sym)->set_symsize(data_size); } + + if (parameters->doing_static_link() + && (this->plt_ == NULL || !this->plt_->has_irelative_section())) + { + // If linking statically, make sure that the __rela_iplt symbols + // were defined if necessary, even if we didn't create a PLT. + static const Define_symbol_in_segment syms[] = + { + { + "__rela_iplt_start", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_W, // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + Symbol::SEGMENT_START, // offset_from_base + true // only_if_ref + }, + { + "__rela_iplt_end", // name + elfcpp::PT_LOAD, // segment_type + elfcpp::PF_W, // segment_flags_set + elfcpp::PF(0), // segment_flags_clear + 0, // value + 0, // size + elfcpp::STT_NOTYPE, // type + elfcpp::STB_GLOBAL, // binding + elfcpp::STV_HIDDEN, // visibility + 0, // nonvis + Symbol::SEGMENT_START, // offset_from_base + true // only_if_ref + } + }; + + symtab->define_symbols(layout, 2, syms, + layout->script_options()->saw_sections_clause()); + } } // Perform a relocation. @@ -2559,7 +2882,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, if (gsym != NULL && gsym->use_plt_offset(Scan::get_reference_flags(r_type))) { - symval.set_output_value(target->plt_section()->address() + symval.set_output_value(target->plt_address_for_global(gsym) + gsym->plt_offset()); psymval = &symval; } @@ -2568,7 +2891,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, unsigned int r_sym = elfcpp::elf_r_sym<64>(rela.get_r_info()); if (object->local_has_plt_offset(r_sym)) { - symval.set_output_value(target->plt_section()->address() + symval.set_output_value(target->plt_address_for_local(object, r_sym) + object->local_plt_offset(r_sym)); psymval = &symval; } @@ -2827,7 +3150,12 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, } 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, rela, r_type, value, view, view_size); @@ -2853,7 +3181,12 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, } if (optimized_type == tls::TLSOPT_TO_IE) { - gold_assert(tls_segment != NULL); + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return; + } value = target->got_plt_section()->address() + got_offset; this->tls_gd_to_ie(relinfo, relnum, tls_segment, rela, r_type, value, view, address, view_size); @@ -2882,7 +3215,12 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, } 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, rela, r_type, value, view, view_size); @@ -2917,7 +3255,12 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, } if (optimized_type == tls::TLSOPT_TO_IE) { - gold_assert(tls_segment != NULL); + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return; + } value = target->got_plt_section()->address() + got_offset; this->tls_desc_gd_to_ie(relinfo, relnum, tls_segment, rela, r_type, value, view, address, @@ -2949,7 +3292,12 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, } 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, rela, r_type, value, view, view_size); break; @@ -2979,7 +3327,12 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, // R_X86_64_TLSLD. if (optimized_type == tls::TLSOPT_TO_LE && is_executable) { - 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(); } Relocate_functions<64, false>::rela32(view, value, addend); @@ -2989,7 +3342,12 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, // See R_X86_64_DTPOFF32, just above, for why we check for is_executable. if (optimized_type == tls::TLSOPT_TO_LE && is_executable) { - 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(); } Relocate_functions<64, false>::rela64(view, value, addend); @@ -2998,7 +3356,12 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec 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_x86_64::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment, rela, r_type, value, view, view_size); @@ -3033,6 +3396,12 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, break; case elfcpp::R_X86_64_TPOFF32: // Local-exec + if (tls_segment == NULL) + { + gold_assert(parameters->errors()->error_count() > 0 + || issue_undefined_symbol_error(gsym)); + return; + } value -= tls_segment->memsz(); Relocate_functions<64, false>::rela32(view, value, addend); break; @@ -3478,7 +3847,7 @@ uint64_t Target_x86_64::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) + gsym->plt_offset(); } // Return a string used to fill a code section with nops to take up @@ -3563,6 +3932,21 @@ Target_x86_64::do_reloc_addend(void* arg, unsigned int r_type, return psymval->value(ti.object, 0); } +// Return the value to use for the base of a DW_EH_PE_datarel offset +// in an FDE. Solaris and SVR4 use DW_EH_PE_datarel because their +// assembler can not write out the difference between two labels in +// different sections, so instead of using a pc-relative value they +// use an offset from the GOT. + +uint64_t +Target_x86_64::do_ehframe_datarel_base() const +{ + gold_assert(this->global_offset_table_ != NULL); + Symbol* sym = this->global_offset_table_; + Sized_symbol<64>* ssym = static_cast*>(sym); + return ssym->value(); +} + // FNOFFSET in section SHNDX in OBJECT is the start of a function // compiled with -fsplit-stack. The function calls non-split-stack // code. We have to change the function so that it always ensures @@ -3631,7 +4015,7 @@ class Target_selector_x86_64 : public Target_selector_freebsd public: Target_selector_x86_64() : Target_selector_freebsd(elfcpp::EM_X86_64, 64, false, "elf64-x86-64", - "elf64-x86-64-freebsd") + "elf64-x86-64-freebsd", "elf_x86_64") { } Target*