X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gold%2Fx86_64.cc;h=0614e89c6064acc7b7f707ac86b6fcb3b1d89488;hb=601e4987b8dd2639159d387a00b4504744c4abfd;hp=34a53ac2951274129bb911c0f028ae74da4b28ac;hpb=72ec2876615bc14c34c3cbddcf6bebbbf8885b94;p=binutils-gdb.git diff --git a/gold/x86_64.cc b/gold/x86_64.cc index 34a53ac2951..0614e89c606 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -83,9 +83,10 @@ class Target_x86_64 : public Sized_target<64, false> 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, - Symbol** global_symbols); + const unsigned char* plocal_symbols); // Finalize the sections. void @@ -102,6 +103,8 @@ class Target_x86_64 : public Sized_target<64, false> unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<64>::Elf_Addr view_address, off_t view_size); @@ -110,6 +113,14 @@ class Target_x86_64 : public Sized_target<64, false> std::string do_code_fill(off_t length); + // Return the size of the GOT section. + off_t + got_size() + { + gold_assert(this->got_ != NULL); + return this->got_->data_size(); + } + private: // The class which scans relocations. struct Scan @@ -174,27 +185,27 @@ class Target_x86_64 : public Sized_target<64, false> const Symbol_value<64>*, unsigned char*, elfcpp::Elf_types<64>::Elf_Addr, off_t); - // Do a TLS Initial-Exec to Local-Exec transition. - static inline void - tls_ie_to_le(const Relocate_info<64, false>*, size_t relnum, + // Do a TLS General-Dynamic to Local-Exec transition. + inline void + tls_gd_to_le(const Relocate_info<64, false>*, size_t relnum, Output_segment* tls_segment, const elfcpp::Rela<64, false>&, unsigned int r_type, elfcpp::Elf_types<64>::Elf_Addr value, unsigned char* view, off_t view_size); - // Do a TLS General-Dynamic to Local-Exec transition. + // Do a TLS Local-Dynamic to Local-Exec transition. inline void - tls_gd_to_le(const Relocate_info<64, false>*, size_t relnum, + tls_ld_to_le(const Relocate_info<64, false>*, size_t relnum, Output_segment* tls_segment, const elfcpp::Rela<64, false>&, unsigned int r_type, elfcpp::Elf_types<64>::Elf_Addr value, unsigned char* view, off_t view_size); - // Do a TLS Local-Dynamic to Local-Exec transition. - inline void - tls_ld_to_le(const Relocate_info<64, false>*, size_t relnum, + // Do a TLS Initial-Exec to Local-Exec transition. + static inline void + tls_ie_to_le(const Relocate_info<64, false>*, size_t relnum, Output_segment* tls_segment, const elfcpp::Rela<64, false>&, unsigned int r_type, elfcpp::Elf_types<64>::Elf_Addr value, @@ -215,6 +226,14 @@ class Target_x86_64 : public Sized_target<64, false> Output_data_got<64, false>* got_section(Symbol_table*, Layout*); + // Get the GOT PLT section. + Output_data_space* + got_plt_section() const + { + gold_assert(this->got_plt_ != NULL); + return this->got_plt_; + } + // Create a PLT entry for a global symbol. void make_plt_entry(Symbol_table*, Layout*, Symbol*); @@ -231,6 +250,17 @@ class Target_x86_64 : public Sized_target<64, false> Reloc_section* rela_dyn_section(Layout*); + // Return true if the symbol may need a COPY relocation. + // References from an executable object to non-function symbols + // defined in a dynamic object may need a COPY relocation. + bool + may_need_copy_reloc(Symbol* gsym) + { + return (!parameters->output_is_shared() + && gsym->is_from_dynobj() + && gsym->type() != elfcpp::STT_FUNC); + } + // Copy a relocation against a global symbol. void copy_reloc(const General_options*, Symbol_table*, Layout*, @@ -263,8 +293,9 @@ const Target::Target_info Target_x86_64::x86_64_info = false, // has_make_symbol false, // has_resolve true, // has_code_fill + true, // is_default_stack_executable "/lib/ld64.so.1", // program interpreter - 0x400000, // text_segment_address + 0x400000, // default_text_segment_address 0x1000, // abi_pagesize 0x1000 // common_pagesize }; @@ -288,7 +319,6 @@ Target_x86_64::got_section(Symbol_table* symtab, Layout* layout) // create another set of data in the .got section. Note that we // always create a PLT if we create a GOT, although the PLT // might be empty. - // TODO(csilvers): do we really need an alignment of 8? this->got_plt_ = new Output_data_space(8); layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE, @@ -381,7 +411,6 @@ class Output_data_plt_x86_64 : public Output_section_data Output_data_plt_x86_64::Output_data_plt_x86_64(Layout* layout, Output_data_space* got_plt) - // TODO(csilvers): do we really need an alignment of 8? : Output_section_data(8), got_plt_(got_plt), count_(0) { this->rel_ = new Reloc_section(); @@ -621,14 +650,9 @@ Target_x86_64::copy_reloc(const General_options* options, off_t offset = dynbss_size; dynbss->set_space_size(dynbss_size + symsize); - // Define the symbol in the .dynbss section. - symtab->define_in_output_data(this, ssym->name(), ssym->version(), - dynbss, offset, symsize, ssym->type(), - ssym->binding(), ssym->visibility(), - ssym->nonvis(), false, false); + symtab->define_with_copy_reloc(this, ssym, dynbss, offset); // Add the COPY reloc. - ssym->set_needs_dynsym_entry(); Reloc_section* rela_dyn = this->rela_dyn_section(layout); rela_dyn->add_global(ssym, elfcpp::R_X86_64_COPY, dynbss, offset, 0); } @@ -721,13 +745,38 @@ Target_x86_64::Scan::local(const General_options&, break; case elfcpp::R_X86_64_64: + // If building a shared library (or a position-independent + // executable), we need to create a dynamic relocation for + // this location. The relocation applied at link time will + // apply the link-time value, so we flag the location with + // an R_386_RELATIVE relocation so the dynamic loader can + // relocate it easily. + if (parameters->output_is_position_independent()) + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, + data_shndx, reloc.get_r_offset(), 0); + } + break; + case elfcpp::R_X86_64_32: case elfcpp::R_X86_64_32S: case elfcpp::R_X86_64_16: case elfcpp::R_X86_64_8: - // FIXME: If we are generating a shared object we need to copy - // this relocation into the object. - gold_assert(!parameters->output_is_shared()); + // If building a shared library (or a position-independent + // executable), we need to create a dynamic relocation for + // this location. The relocation applied at link time will + // apply the link-time value, so we flag the location with + // an R_386_RELATIVE relocation so the dynamic loader can + // relocate it easily. + if (parameters->output_is_position_independent()) + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + unsigned int r_sym = elfcpp::elf_r_sym<64>(reloc.get_r_info()); + rela_dyn->add_local(object, r_sym, r_type, data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend()); + } break; case elfcpp::R_X86_64_PC64: @@ -736,18 +785,26 @@ Target_x86_64::Scan::local(const General_options&, case elfcpp::R_X86_64_PC8: break; - case elfcpp::R_X86_64_GOTPC32: // TODO(csilvers): correct? + case elfcpp::R_X86_64_PLT32: + // Since we know this is a local symbol, we can handle this as a + // PC32 reloc. + break; + + case elfcpp::R_X86_64_GOTPC32: case elfcpp::R_X86_64_GOTOFF64: - case elfcpp::R_X86_64_GOTPC64: // TODO(csilvers): correct? - case elfcpp::R_X86_64_PLTOFF64: // TODO(csilvers): correct? + case elfcpp::R_X86_64_GOTPC64: + case elfcpp::R_X86_64_PLTOFF64: // We need a GOT section. target->got_section(symtab, layout); + // For PLTOFF64, we'd normally want a PLT section, but since we + // know this is a local symbol, no PLT is needed. break; 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: { // The symbol requires a GOT entry. Output_data_got<64, false>* got = target->got_section(symtab, layout); @@ -756,13 +813,18 @@ Target_x86_64::Scan::local(const General_options&, { // If we are generating a shared object, we need to add a // dynamic RELATIVE relocation for this symbol. - if (parameters->output_is_shared()) + if (parameters->output_is_position_independent()) { + // FIXME: R_X86_64_RELATIVE assumes a 64-bit relocation. + gold_assert(r_type != elfcpp::R_X86_64_GOT32); + Reloc_section* rela_dyn = target->rela_dyn_section(layout); rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, data_shndx, reloc.get_r_offset(), 0); } } + // For GOTPLT64, we'd normally want a PLT section, but since + // we know this is a local symbol, no PLT is needed. } break; @@ -779,33 +841,29 @@ Target_x86_64::Scan::local(const General_options&, break; // These are initial tls relocs, which are expected when linking - case elfcpp::R_X86_64_TLSGD: - case elfcpp::R_X86_64_GOTPC32_TLSDESC: + case elfcpp::R_X86_64_TLSGD: // Global-dynamic + case elfcpp::R_X86_64_GOTPC32_TLSDESC: // Global-dynamic (from ~oliva url) case elfcpp::R_X86_64_TLSDESC_CALL: - case elfcpp::R_X86_64_TLSLD: - case elfcpp::R_X86_64_GOTTPOFF: - case elfcpp::R_X86_64_TPOFF32: + case elfcpp::R_X86_64_TLSLD: // Local-dynamic case elfcpp::R_X86_64_DTPOFF32: case elfcpp::R_X86_64_DTPOFF64: + case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec + case elfcpp::R_X86_64_TPOFF32: // Local-exec { bool output_is_shared = parameters->output_is_shared(); const tls::Tls_optimization optimized_type = Target_x86_64::optimize_tls_reloc(!output_is_shared, r_type); switch (r_type) { - case elfcpp::R_X86_64_TPOFF32: // Local-exec - // FIXME: If generating a shared object, we need to copy - // this relocation into the object. - gold_assert(!output_is_shared); + case elfcpp::R_X86_64_TLSGD: // General-dynamic + case elfcpp::R_X86_64_GOTPC32_TLSDESC: + case elfcpp::R_X86_64_TLSDESC_CALL: + // FIXME: If not relaxing to LE, we need to generate + // DTPMOD64 and DTPOFF64 relocs. + if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_local(object, r_type); break; - case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec - // FIXME: If not relaxing to LE, we need to generate a - // TPOFF64 reloc. - if (optimized_type != tls::TLSOPT_TO_LE) - unsupported_reloc_local(object, r_type); - break; - case elfcpp::R_X86_64_TLSLD: // Local-dynamic case elfcpp::R_X86_64_DTPOFF32: case elfcpp::R_X86_64_DTPOFF64: @@ -815,14 +873,17 @@ Target_x86_64::Scan::local(const General_options&, unsupported_reloc_local(object, r_type); break; + case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec + // FIXME: If not relaxing to LE, we need to generate a + // TPOFF64 reloc. + if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_local(object, r_type); + break; - case elfcpp::R_X86_64_TLSGD: // General-dynamic - case elfcpp::R_X86_64_GOTPC32_TLSDESC: - case elfcpp::R_X86_64_TLSDESC_CALL: - // FIXME: If not relaxing to LE, we need to generate - // DTPMOD64 and DTPOFF64 relocs. - if (optimized_type != tls::TLSOPT_TO_LE) - unsupported_reloc_local(object, r_type); + case elfcpp::R_X86_64_TPOFF32: // Local-exec + // FIXME: If generating a shared object, we need to copy + // this relocation into the object. + gold_assert(!output_is_shared); break; default: @@ -831,10 +892,8 @@ Target_x86_64::Scan::local(const General_options&, } break; - case elfcpp::R_X86_64_GOTPLT64: - case elfcpp::R_X86_64_PLT32: - case elfcpp::R_X86_64_SIZE32: // TODO(csilvers): correct? - case elfcpp::R_X86_64_SIZE64: // TODO(csilvers): correct? + case elfcpp::R_X86_64_SIZE32: + case elfcpp::R_X86_64_SIZE64: default: gold_error(_("%s: unsupported reloc %u against local symbol"), object->name().c_str(), r_type); @@ -875,44 +934,75 @@ Target_x86_64::Scan::global(const General_options& options, break; case elfcpp::R_X86_64_64: - case elfcpp::R_X86_64_PC64: case elfcpp::R_X86_64_32: case elfcpp::R_X86_64_32S: - case elfcpp::R_X86_64_PC32: case elfcpp::R_X86_64_16: - case elfcpp::R_X86_64_PC16: case elfcpp::R_X86_64_8: - case elfcpp::R_X86_64_PC8: - // FIXME: If we are generating a shared object we may need to - // copy this relocation into the object. If this symbol is - // defined in a shared object, we may need to copy this - // relocation in order to avoid a COPY relocation. - gold_assert(!parameters->output_is_shared()); - - if (gsym->is_from_dynobj()) - { - // This symbol is defined in a dynamic object. If it is a - // function, we make a PLT entry. Otherwise we need to - // either generate a COPY reloc or copy this reloc. - if (gsym->type() == elfcpp::STT_FUNC) - { - target->make_plt_entry(symtab, layout, gsym); - - // If this is not a PC relative reference, then we may - // be taking the address of the function. In that case - // we need to set the entry in the dynamic symbol table - // to the address of the PLT entry. - if (r_type != elfcpp::R_X86_64_PC64 - && r_type != elfcpp::R_X86_64_PC32 - && r_type != elfcpp::R_X86_64_PC16 - && r_type != elfcpp::R_X86_64_PC8) - gsym->set_needs_dynsym_value(); - } - else - target->copy_reloc(&options, symtab, layout, object, data_shndx, - 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()) + gsym->set_needs_dynsym_value(); + } + // Make a dynamic relocation if necessary. + if (gsym->needs_dynamic_reloc(true, false)) + { + if (target->may_need_copy_reloc(gsym)) + { + target->copy_reloc(&options, symtab, layout, object, data_shndx, + gsym, reloc); + } + else if (r_type == elfcpp::R_X86_64_64 + && gsym->can_use_relative_reloc(false)) + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, + data_shndx, + reloc.get_r_offset(), 0); + } + else + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_global(gsym, r_type, object, data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend()); + } + } + } + break; + case elfcpp::R_X86_64_PC64: + case elfcpp::R_X86_64_PC32: + case elfcpp::R_X86_64_PC16: + case elfcpp::R_X86_64_PC8: + { + // Make a PLT entry if necessary. + if (gsym->needs_plt_entry()) + target->make_plt_entry(symtab, layout, gsym); + // Make a dynamic relocation if necessary. + bool is_function_call = (gsym->type() == elfcpp::STT_FUNC); + if (gsym->needs_dynamic_reloc(true, is_function_call)) + { + if (target->may_need_copy_reloc(gsym)) + { + target->copy_reloc(&options, symtab, layout, object, data_shndx, + gsym, reloc); + } + else + { + Reloc_section* rela_dyn = target->rela_dyn_section(layout); + rela_dyn->add_global(gsym, r_type, object, data_shndx, + reloc.get_r_offset(), + reloc.get_r_addend()); + } + } + } break; case elfcpp::R_X86_64_GOT64: @@ -930,10 +1020,24 @@ Target_x86_64::Scan::global(const General_options& options, if (!gsym->final_value_is_known()) { Reloc_section* rela_dyn = target->rela_dyn_section(layout); - rela_dyn->add_global(gsym, elfcpp::R_X86_64_GLOB_DAT, got, - gsym->got_offset(), 0); + if (gsym->is_from_dynobj() + || gsym->is_preemptible()) + rela_dyn->add_global(gsym, elfcpp::R_X86_64_GLOB_DAT, got, + gsym->got_offset(), 0); + else + { + rela_dyn->add_local(object, 0, elfcpp::R_X86_64_RELATIVE, + got, gsym->got_offset(), 0); + // Make sure we write the link-time value to the GOT. + gsym->set_needs_value_in_got(); + } } } + // For GOTPLT64, we also need a PLT entry (but only if the + // symbol is not fully resolved). + if (r_type == elfcpp::R_X86_64_GOTPLT64 + && !gsym->final_value_is_known()) + target->make_plt_entry(symtab, layout, gsym); } break; @@ -942,15 +1046,27 @@ Target_x86_64::Scan::global(const General_options& options, // Otherwise we need a PLT entry. if (gsym->final_value_is_known()) break; + // If building a shared library, we can also skip the PLT entry + // 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()) + break; target->make_plt_entry(symtab, layout, gsym); break; - case elfcpp::R_X86_64_GOTPC32: // TODO(csilvers): correct? + case elfcpp::R_X86_64_GOTPC32: case elfcpp::R_X86_64_GOTOFF64: - case elfcpp::R_X86_64_GOTPC64: // TODO(csilvers): correct? - case elfcpp::R_X86_64_PLTOFF64: // TODO(csilvers): correct? + case elfcpp::R_X86_64_GOTPC64: + case elfcpp::R_X86_64_PLTOFF64: // We need a GOT section. target->got_section(symtab, layout); + // For PLTOFF64, we also need a PLT entry (but only if the + // symbol is not fully resolved). + if (r_type == elfcpp::R_X86_64_PLTOFF64 + && !gsym->final_value_is_known()) + target->make_plt_entry(symtab, layout, gsym); break; case elfcpp::R_X86_64_COPY: @@ -966,33 +1082,29 @@ Target_x86_64::Scan::global(const General_options& options, break; // These are initial tls relocs, which are expected for global() - case elfcpp::R_X86_64_TLSGD: - case elfcpp::R_X86_64_TLSLD: - case elfcpp::R_X86_64_GOTTPOFF: - case elfcpp::R_X86_64_TPOFF32: - case elfcpp::R_X86_64_GOTPC32_TLSDESC: + case elfcpp::R_X86_64_TLSGD: // Global-dynamic + case elfcpp::R_X86_64_GOTPC32_TLSDESC: // Global-dynamic (from ~oliva url) case elfcpp::R_X86_64_TLSDESC_CALL: + case elfcpp::R_X86_64_TLSLD: // Local-dynamic case elfcpp::R_X86_64_DTPOFF32: case elfcpp::R_X86_64_DTPOFF64: + case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec + case elfcpp::R_X86_64_TPOFF32: // Local-exec { const bool is_final = gsym->final_value_is_known(); const tls::Tls_optimization optimized_type = Target_x86_64::optimize_tls_reloc(is_final, r_type); switch (r_type) { - case elfcpp::R_X86_64_TPOFF32: // Local-exec - // FIXME: If generating a shared object, we need to copy - // this relocation into the object. - gold_assert(is_final); + case elfcpp::R_X86_64_TLSGD: // General-dynamic + case elfcpp::R_X86_64_GOTPC32_TLSDESC: + case elfcpp::R_X86_64_TLSDESC_CALL: + // FIXME: If not relaxing to LE, we need to generate + // DTPMOD64 and DTPOFF64, or TLSDESC, relocs. + if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_global(object, r_type, gsym); break; - case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec - // FIXME: If not relaxing to LE, we need to generate a - // TPOFF64 reloc. - if (optimized_type != tls::TLSOPT_TO_LE) - unsupported_reloc_global(object, r_type, gsym); - break; - case elfcpp::R_X86_64_TLSLD: // Local-dynamic case elfcpp::R_X86_64_DTPOFF32: case elfcpp::R_X86_64_DTPOFF64: @@ -1002,14 +1114,17 @@ Target_x86_64::Scan::global(const General_options& options, unsupported_reloc_global(object, r_type, gsym); break; + case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec + // FIXME: If not relaxing to LE, we need to generate a + // TPOFF64 reloc. + if (optimized_type != tls::TLSOPT_TO_LE) + unsupported_reloc_global(object, r_type, gsym); + break; - case elfcpp::R_X86_64_TLSGD: // General-dynamic - case elfcpp::R_X86_64_GOTPC32_TLSDESC: - case elfcpp::R_X86_64_TLSDESC_CALL: - // FIXME: If not relaxing to LE, we need to generate - // DTPMOD64 and DTPOFF64, or TLSDESC, relocs. - if (optimized_type != tls::TLSOPT_TO_LE) - unsupported_reloc_global(object, r_type, gsym); + case elfcpp::R_X86_64_TPOFF32: // Local-exec + // FIXME: If generating a shared object, we need to copy + // this relocation into the object. + gold_assert(is_final); break; default: @@ -1017,8 +1132,9 @@ Target_x86_64::Scan::global(const General_options& options, } } break; - case elfcpp::R_X86_64_SIZE32: // TODO(csilvers): correct? - case elfcpp::R_X86_64_SIZE64: // TODO(csilvers): correct? + + case elfcpp::R_X86_64_SIZE32: + case elfcpp::R_X86_64_SIZE64: default: gold_error(_("%s: unsupported reloc %u against global symbol %s"), object->name().c_str(), r_type, gsym->name()); @@ -1037,9 +1153,10 @@ Target_x86_64::scan_relocs(const General_options& options, 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, - Symbol** global_symbols) + const unsigned char* plocal_symbols) { if (sh_type == elfcpp::SHT_REL) { @@ -1058,9 +1175,10 @@ Target_x86_64::scan_relocs(const General_options& options, data_shndx, prelocs, reloc_count, + output_section, + needs_special_offset_handling, local_symbol_count, - plocal_symbols, - global_symbols); + plocal_symbols); } // Finalize the sections. @@ -1145,7 +1263,11 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, // Pick the value to use for symbols defined in shared objects. Symbol_value<64> symval; - if (gsym != NULL && gsym->is_from_dynobj() && gsym->has_plt_offset()) + if (gsym != NULL + && (gsym->is_from_dynobj() + || (parameters->output_is_shared() + && gsym->is_preemptible())) + && gsym->has_plt_offset()) { symval.set_output_value(target->plt_section()->address() + gsym->plt_offset()); @@ -1156,6 +1278,9 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, const elfcpp::Elf_Xword addend = rela.get_r_addend(); // 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) @@ -1168,12 +1293,12 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, if (gsym != NULL) { gold_assert(gsym->has_got_offset()); - got_offset = gsym->got_offset(); + got_offset = gsym->got_offset() - target->got_size(); } else { unsigned int r_sym = elfcpp::elf_r_sym<64>(rela.get_r_info()); - got_offset = object->local_got_offset(r_sym); + got_offset = object->local_got_offset(r_sym) - target->got_size(); } have_got_offset = true; break; @@ -1181,7 +1306,6 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, default: break; } - switch (r_type) { @@ -1238,12 +1362,27 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, break; case elfcpp::R_X86_64_PLT32: - gold_assert(gsym->has_plt_offset() + gold_assert(gsym == NULL + || gsym->has_plt_offset() || gsym->final_value_is_known()); + // Note: while this code looks the same as for R_X86_64_PC32, it + // behaves differently because psymval was set to point to + // the PLT entry, rather than the symbol, in Scan::global(). Relocate_functions<64, false>::pcrela32(view, object, psymval, addend, address); break; + case elfcpp::R_X86_64_PLTOFF64: + { + gold_assert(gsym); + gold_assert(gsym->has_plt_offset() + || gsym->final_value_is_known()); + elfcpp::Elf_types<64>::Elf_Addr got_address; + got_address = target->got_section(NULL, NULL)->address(); + Relocate_functions<64, false>::rela64(view, object, psymval, + addend - got_address); + } + case elfcpp::R_X86_64_GOT32: gold_assert(have_got_offset); Relocate_functions<64, false>::rela32(view, got_offset, addend); @@ -1253,7 +1392,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, { gold_assert(gsym); elfcpp::Elf_types<64>::Elf_Addr value; - value = target->got_section(NULL, NULL)->address(); + value = target->got_plt_section()->address(); Relocate_functions<64, false>::pcrela32(view, value, addend, address); } break; @@ -1261,7 +1400,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, case elfcpp::R_X86_64_GOT64: // The ABI doc says "Like GOT64, but indicates a PLT entry is needed." // Since we always add a PLT entry, this is equivalent. - case elfcpp::R_X86_64_GOTPLT64: // TODO(csilvers): correct? + case elfcpp::R_X86_64_GOTPLT64: gold_assert(have_got_offset); Relocate_functions<64, false>::rela64(view, got_offset, addend); break; @@ -1270,7 +1409,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, { gold_assert(gsym); elfcpp::Elf_types<64>::Elf_Addr value; - value = target->got_section(NULL, NULL)->address(); + value = target->got_plt_section()->address(); Relocate_functions<64, false>::pcrela64(view, value, addend, address); } break; @@ -1279,7 +1418,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, { elfcpp::Elf_types<64>::Elf_Addr value; value = (psymval->value(object, 0) - - target->got_section(NULL, NULL)->address()); + - target->got_plt_section()->address()); Relocate_functions<64, false>::rela64(view, value, addend); } break; @@ -1288,7 +1427,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, { gold_assert(have_got_offset); elfcpp::Elf_types<64>::Elf_Addr value; - value = target->got_section(NULL, NULL)->address() + got_offset; + value = target->got_plt_section()->address() + got_offset; Relocate_functions<64, false>::pcrela32(view, value, addend, address); } break; @@ -1297,7 +1436,7 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, { gold_assert(have_got_offset); elfcpp::Elf_types<64>::Elf_Addr value; - value = target->got_section(NULL, NULL)->address() + got_offset; + value = target->got_plt_section()->address() + got_offset; Relocate_functions<64, false>::pcrela64(view, value, addend, address); } break; @@ -1316,21 +1455,20 @@ Target_x86_64::Relocate::relocate(const Relocate_info<64, false>* relinfo, break; // These are initial tls relocs, which are expected when linking - case elfcpp::R_X86_64_TLSGD: - case elfcpp::R_X86_64_TLSLD: - case elfcpp::R_X86_64_GOTTPOFF: - case elfcpp::R_X86_64_TPOFF32: - case elfcpp::R_X86_64_GOTPC32_TLSDESC: + case elfcpp::R_X86_64_TLSGD: // Global-dynamic + case elfcpp::R_X86_64_GOTPC32_TLSDESC: // Global-dynamic (from ~oliva url) case elfcpp::R_X86_64_TLSDESC_CALL: + case elfcpp::R_X86_64_TLSLD: // Local-dynamic case elfcpp::R_X86_64_DTPOFF32: case elfcpp::R_X86_64_DTPOFF64: + case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec + case elfcpp::R_X86_64_TPOFF32: // Local-exec this->relocate_tls(relinfo, relnum, rela, r_type, gsym, psymval, view, address, view_size); break; - case elfcpp::R_X86_64_SIZE32: // TODO(csilvers): correct? - case elfcpp::R_X86_64_SIZE64: // TODO(csilvers): correct? - case elfcpp::R_X86_64_PLTOFF64: // TODO(csilvers): implement me! + case elfcpp::R_X86_64_SIZE32: + case elfcpp::R_X86_64_SIZE64: default: gold_error_at_location(relinfo, relnum, rela.get_r_offset(), _("unsupported reloc %u"), @@ -1365,32 +1503,14 @@ 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->output_is_shared() + ? !parameters->output_is_position_independent() : gsym->final_value_is_known()); const tls::Tls_optimization optimized_type = Target_x86_64::optimize_tls_reloc(is_final, r_type); switch (r_type) { - case elfcpp::R_X86_64_TPOFF32: // Local-exec reloc - value = value - (tls_segment->vaddr() + tls_segment->memsz()); - Relocate_functions<64, false>::rel32(view, value); - break; - - case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec reloc - if (optimized_type == tls::TLSOPT_TO_LE) - { - Target_x86_64::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment, - rela, r_type, value, view, - view_size); - break; - } - gold_error_at_location(relinfo, relnum, rela.get_r_offset(), - _("unsupported reloc type %u"), - r_type); - break; - - case elfcpp::R_X86_64_TLSGD: - case elfcpp::R_X86_64_GOTPC32_TLSDESC: + case elfcpp::R_X86_64_TLSGD: // Global-dynamic + case elfcpp::R_X86_64_GOTPC32_TLSDESC: // Global-dynamic (from ~oliva url) case elfcpp::R_X86_64_TLSDESC_CALL: if (optimized_type == tls::TLSOPT_TO_LE) { @@ -1403,7 +1523,7 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, _("unsupported reloc %u"), r_type); break; - case elfcpp::R_X86_64_TLSLD: + case elfcpp::R_X86_64_TLSLD: // Local-dynamic if (optimized_type == tls::TLSOPT_TO_LE) { this->tls_ld_to_le(relinfo, relnum, tls_segment, rela, r_type, @@ -1429,63 +1549,25 @@ Target_x86_64::Relocate::relocate_tls(const Relocate_info<64, false>* relinfo, value = value - tls_segment->vaddr(); Relocate_functions<64, false>::rel64(view, value); break; - } -} - -// Do a relocation in which we convert a TLS Initial-Exec to a -// Local-Exec. - -inline void -Target_x86_64::Relocate::tls_ie_to_le(const Relocate_info<64, false>* relinfo, - size_t relnum, - Output_segment* tls_segment, - const elfcpp::Rela<64, false>& rela, - unsigned int, - elfcpp::Elf_types<64>::Elf_Addr value, - unsigned char* view, - off_t view_size) -{ - // We need to examine the opcodes to figure out which instruction we - // are looking at. - - // movq foo@gottpoff(%rip),%reg ==> movq $YY,%reg - // addq foo@gottpoff(%rip),%reg ==> addq $YY,%reg - - tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -3); - tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4); - unsigned char op1 = view[-3]; - unsigned char op2 = view[-2]; - unsigned char op3 = view[-1]; - unsigned char reg = op3 >> 3; + case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec + if (optimized_type == tls::TLSOPT_TO_LE) + { + Target_x86_64::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment, + rela, r_type, value, view, + view_size); + break; + } + gold_error_at_location(relinfo, relnum, rela.get_r_offset(), + _("unsupported reloc type %u"), + r_type); + break; - if (op2 == 0x8b) - { - // movq - if (op1 == 0x4c) - view[-3] = 0x49; - view[-2] = 0xc7; - view[-1] = 0xc0 | reg; - } - else if (reg == 4) - { - // Special handling for %rsp. - if (op1 == 0x4c) - view[-3] = 0x49; - view[-2] = 0x81; - view[-1] = 0xc0 | reg; - } - else - { - // addq - if (op1 == 0x4c) - view[-3] = 0x4d; - view[-2] = 0x8d; - view[-1] = 0x80 | reg | (reg << 3); + case elfcpp::R_X86_64_TPOFF32: // Local-exec + value = value - (tls_segment->vaddr() + tls_segment->memsz()); + Relocate_functions<64, false>::rel32(view, value); + break; } - - value = value - (tls_segment->vaddr() + tls_segment->memsz()); - Relocate_functions<64, false>::rela32(view, value, 0); } // Do a relocation in which we convert a TLS General-Dynamic to a @@ -1552,6 +1634,62 @@ Target_x86_64::Relocate::tls_ld_to_le(const Relocate_info<64, false>* relinfo, this->skip_call_tls_get_addr_ = true; } +// Do a relocation in which we convert a TLS Initial-Exec to a +// Local-Exec. + +inline void +Target_x86_64::Relocate::tls_ie_to_le(const Relocate_info<64, false>* relinfo, + size_t relnum, + Output_segment* tls_segment, + const elfcpp::Rela<64, false>& rela, + unsigned int, + elfcpp::Elf_types<64>::Elf_Addr value, + unsigned char* view, + off_t view_size) +{ + // We need to examine the opcodes to figure out which instruction we + // are looking at. + + // movq foo@gottpoff(%rip),%reg ==> movq $YY,%reg + // addq foo@gottpoff(%rip),%reg ==> addq $YY,%reg + + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, -3); + tls::check_range(relinfo, relnum, rela.get_r_offset(), view_size, 4); + + unsigned char op1 = view[-3]; + unsigned char op2 = view[-2]; + unsigned char op3 = view[-1]; + unsigned char reg = op3 >> 3; + + if (op2 == 0x8b) + { + // movq + if (op1 == 0x4c) + view[-3] = 0x49; + view[-2] = 0xc7; + view[-1] = 0xc0 | reg; + } + else if (reg == 4) + { + // Special handling for %rsp. + if (op1 == 0x4c) + view[-3] = 0x49; + view[-2] = 0x81; + view[-1] = 0xc0 | reg; + } + else + { + // addq + if (op1 == 0x4c) + view[-3] = 0x4d; + view[-2] = 0x8d; + view[-1] = 0x80 | reg | (reg << 3); + } + + value = value - (tls_segment->vaddr() + tls_segment->memsz()); + Relocate_functions<64, false>::rela32(view, value, 0); +} + // Relocate section data. void @@ -1559,6 +1697,8 @@ Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo, unsigned int sh_type, const unsigned char* prelocs, size_t reloc_count, + Output_section* output_section, + bool needs_special_offset_handling, unsigned char* view, elfcpp::Elf_types<64>::Elf_Addr address, off_t view_size) @@ -1571,6 +1711,8 @@ Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo, this, prelocs, reloc_count, + output_section, + needs_special_offset_handling, view, address, view_size);