X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gold%2Fx86_64.cc;h=f260e74c0bc936f09ebc2c6a8660bca1f615c3ab;hb=3e1a8f9569478157ee932e35de928beb211f5df4;hp=479fb420b986aa2095babe5cad47b74ad5e780f3;hpb=a82bef932ec11cc16f205427f8a056c3c0ea517d;p=deliverable%2Fbinutils-gdb.git diff --git a/gold/x86_64.cc b/gold/x86_64.cc index 479fb420b9..f260e74c0b 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 (C) 2006-2014 Free Software Foundation, Inc. +// Copyright (C) 2006-2015 Free Software Foundation, Inc. // Written by Ian Lance Taylor . // This file is part of gold. @@ -505,7 +505,6 @@ class Target_x86_64 : public Sized_target size_t reloc_count, Output_section* output_section, typename elfcpp::Elf_types::Elf_Off offset_in_output_section, - const Relocatable_relocs*, unsigned char* view, typename elfcpp::Elf_types::Elf_Addr view_address, section_size_type view_size, @@ -562,6 +561,7 @@ class Target_x86_64 : public Sized_target void do_calls_non_split(Relobj* object, unsigned int shndx, section_offset_type fnoffset, section_size_type fnsize, + const unsigned char* prelocs, size_t reloc_count, unsigned char* view, section_size_type view_size, std::string* from, std::string* to) const; @@ -594,6 +594,11 @@ class Target_x86_64 : public Sized_target unsigned int plt_entry_size() const; + // Return the size of each GOT entry. + unsigned int + got_entry_size() const + { return 8; }; + // Create the GOT section for an incremental update. Output_data_got_base* init_got_plt_for_update(Symbol_table* symtab, @@ -777,11 +782,9 @@ class Target_x86_64 : public Sized_target // Do a relocation. Return false if the caller should not issue // any warnings about this relocation. inline bool - relocate(const Relocate_info*, Target_x86_64*, - Output_section*, - size_t relnum, const elfcpp::Rela&, - unsigned int r_type, const Sized_symbol*, - const Symbol_value*, + relocate(const Relocate_info*, unsigned int, + Target_x86_64*, Output_section*, size_t, const unsigned char*, + const Sized_symbol*, const Symbol_value*, unsigned char*, typename elfcpp::Elf_types::Elf_Addr, section_size_type); @@ -865,6 +868,25 @@ class Target_x86_64 : public Sized_target get_size_for_reloc(unsigned int, Relobj*); }; + // Check if relocation against this symbol is a candidate for + // conversion from + // mov foo@GOTPCREL(%rip), %reg + // to lea foo(%rip), %reg. + static bool + can_convert_mov_to_lea(const Symbol* gsym) + { + gold_assert(gsym != NULL); + return (gsym->type() != elfcpp::STT_GNU_IFUNC + && !gsym->is_undefined () + && !gsym->is_from_dynobj() + && !gsym->is_preemptible() + && (!parameters->options().shared() + || (gsym->visibility() != elfcpp::STV_DEFAULT + && gsym->visibility() != elfcpp::STV_PROTECTED) + || parameters->options().Bsymbolic()) + && strcmp(gsym->name(), "_DYNAMIC") != 0); + } + // Adjust TLS relocation type based on the options and whether this // is a local symbol. static tls::Tls_optimization @@ -944,10 +966,13 @@ class Target_x86_64 : public Sized_target unsigned int shndx, Output_section* output_section, Symbol* sym, const elfcpp::Rela& reloc) { + unsigned int r_type = elfcpp::elf_r_type(reloc.get_r_info()); this->copy_relocs_.copy_reloc(symtab, layout, symtab->get_sized_symbol(sym), object, shndx, output_section, - reloc, this->rela_dyn_section(layout)); + r_type, reloc.get_r_offset(), + reloc.get_r_addend(), + this->rela_dyn_section(layout)); } // Information about this specific target which we pass to the @@ -1033,7 +1058,8 @@ const Target::Target_info Target_x86_64<64>::x86_64_info = elfcpp::SHF_X86_64_LARGE, // large_common_section_flags NULL, // attributes_section NULL, // attributes_vendor - "_start" // entry_symbol_name + "_start", // entry_symbol_name + 32, // hash_entry_size }; template<> @@ -1060,7 +1086,8 @@ const Target::Target_info Target_x86_64<32>::x86_64_info = elfcpp::SHF_X86_64_LARGE, // large_common_section_flags NULL, // attributes_section NULL, // attributes_vendor - "_start" // entry_symbol_name + "_start", // entry_symbol_name + 32, // hash_entry_size }; // This is called when a new output section is created. This is where @@ -1516,11 +1543,16 @@ Output_data_plt_x86_64_standard::do_fill_plt_entry( unsigned int plt_offset, unsigned int plt_index) { + // Check PC-relative offset overflow in PLT entry. + uint64_t plt_got_pcrel_offset = (got_address + got_offset + - (plt_address + plt_offset + 6)); + if (Bits<32>::has_overflow(plt_got_pcrel_offset)) + gold_error(_("PC-relative offset overflow in PLT entry %d"), + plt_index + 1); + memcpy(pov, plt_entry, plt_entry_size); elfcpp::Swap_unaligned<32, false>::writeval(pov + 2, - (got_address + got_offset - - (plt_address + plt_offset - + 6))); + plt_got_pcrel_offset); elfcpp::Swap_unaligned<32, false>::writeval(pov + 7, plt_index); elfcpp::Swap<32, false>::writeval(pov + 12, @@ -2167,6 +2199,8 @@ Target_x86_64::Scan::get_reference_flags(unsigned int r_type) case elfcpp::R_X86_64_GOT32: case elfcpp::R_X86_64_GOTPCREL64: case elfcpp::R_X86_64_GOTPCREL: + case elfcpp::R_X86_64_GOTPCRELX: + case elfcpp::R_X86_64_REX_GOTPCRELX: case elfcpp::R_X86_64_GOTPLT64: // Absolute in GOT. return Symbol::ABSOLUTE_REF; @@ -2451,10 +2485,33 @@ Target_x86_64::Scan::local(Symbol_table* symtab, case elfcpp::R_X86_64_GOT32: case elfcpp::R_X86_64_GOTPCREL64: case elfcpp::R_X86_64_GOTPCREL: + case elfcpp::R_X86_64_GOTPCRELX: + case elfcpp::R_X86_64_REX_GOTPCRELX: case elfcpp::R_X86_64_GOTPLT64: { - // The symbol requires a GOT entry. + // The symbol requires a GOT section. Output_data_got<64, false>* got = target->got_section(symtab, layout); + + // If the relocation symbol isn't IFUNC, + // and is local, then we will convert + // mov foo@GOTPCREL(%rip), %reg + // to lea foo(%rip), %reg. + // in Relocate::relocate. + if ((r_type == elfcpp::R_X86_64_GOTPCREL + || r_type == elfcpp::R_X86_64_GOTPCRELX + || r_type == elfcpp::R_X86_64_REX_GOTPCRELX) + && reloc.get_r_offset() >= 2 + && !is_ifunc) + { + section_size_type stype; + const unsigned char* view = object->section_contents(data_shndx, + &stype, true); + if (view[reloc.get_r_offset() - 2] == 0x8b) + break; + } + + + // The symbol requires a GOT entry. unsigned int r_sym = elfcpp::elf_r_sym(reloc.get_r_info()); // For a STT_GNU_IFUNC symbol we want the PLT offset. That @@ -2670,6 +2727,8 @@ Target_x86_64::Scan::possible_function_pointer_reloc(unsigned int r_type) case elfcpp::R_X86_64_GOT32: case elfcpp::R_X86_64_GOTPCREL64: case elfcpp::R_X86_64_GOTPCREL: + case elfcpp::R_X86_64_GOTPCRELX: + case elfcpp::R_X86_64_REX_GOTPCRELX: case elfcpp::R_X86_64_GOTPLT64: { return true; @@ -2858,10 +2917,30 @@ Target_x86_64::Scan::global(Symbol_table* symtab, case elfcpp::R_X86_64_GOT32: case elfcpp::R_X86_64_GOTPCREL64: case elfcpp::R_X86_64_GOTPCREL: + case elfcpp::R_X86_64_GOTPCRELX: + case elfcpp::R_X86_64_REX_GOTPCRELX: case elfcpp::R_X86_64_GOTPLT64: { // The symbol requires a GOT entry. Output_data_got<64, false>* got = target->got_section(symtab, layout); + + // If we convert this from + // mov foo@GOTPCREL(%rip), %reg + // to lea foo(%rip), %reg. + // in Relocate::relocate, then there is nothing to do here. + if ((r_type == elfcpp::R_X86_64_GOTPCREL + || r_type == elfcpp::R_X86_64_GOTPCRELX + || r_type == elfcpp::R_X86_64_REX_GOTPCRELX) + && reloc.get_r_offset() >= 2 + && Target_x86_64::can_convert_mov_to_lea(gsym)) + { + section_size_type stype; + const unsigned char* view = object->section_contents(data_shndx, + &stype, true); + if (view[reloc.get_r_offset() - 2] == 0x8b) + break; + } + if (gsym->final_value_is_known()) { // For a STT_GNU_IFUNC symbol we want the PLT address. @@ -2923,11 +3002,6 @@ Target_x86_64::Scan::global(Symbol_table* symtab, } } } - // 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; @@ -2983,7 +3057,12 @@ Target_x86_64::Scan::global(Symbol_table* symtab, 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(); + // For the Initial-Exec model, we can treat undef symbols as final + // when building an executable. + const bool is_final = (gsym->final_value_is_known() || + (r_type == elfcpp::R_X86_64_GOTTPOFF && + gsym->is_undefined() && + parameters->options().output_is_executable())); const tls::Tls_optimization optimized_type = Target_x86_64::optimize_tls_reloc(is_final, r_type); switch (r_type) @@ -3267,17 +3346,20 @@ template inline bool Target_x86_64::Relocate::relocate( const Relocate_info* relinfo, + unsigned int, Target_x86_64* target, Output_section*, size_t relnum, - const elfcpp::Rela& rela, - unsigned int r_type, + const unsigned char* preloc, const Sized_symbol* gsym, const Symbol_value* psymval, unsigned char* view, typename elfcpp::Elf_types::Elf_Addr address, section_size_type view_size) { + const elfcpp::Rela rela(preloc); + unsigned int r_type = elfcpp::elf_r_type(rela.get_r_info()); + if (this->skip_call_tls_get_addr_) { if ((r_type != elfcpp::R_X86_64_PLT32 @@ -3327,13 +3409,14 @@ Target_x86_64::Relocate::relocate( // 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; + // Since the actual offset is always negative, we use signed int to + // support 64-bit GOT relocations. + int got_offset = 0; switch (r_type) { case elfcpp::R_X86_64_GOT32: case elfcpp::R_X86_64_GOT64: case elfcpp::R_X86_64_GOTPLT64: - case elfcpp::R_X86_64_GOTPCREL: case elfcpp::R_X86_64_GOTPCREL64: if (gsym != NULL) { @@ -3430,7 +3513,8 @@ Target_x86_64::Relocate::relocate( gold_assert(gsym->has_plt_offset() || gsym->final_value_is_known()); typename elfcpp::Elf_types::Elf_Addr got_address; - got_address = target->got_section(NULL, NULL)->address(); + // This is the address of GLOBAL_OFFSET_TABLE. + got_address = target->got_plt_section()->address(); Relocate_functions::rela64(view, object, psymval, addend - got_address); } @@ -3451,9 +3535,9 @@ Target_x86_64::Relocate::relocate( break; 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: + // R_X86_64_GOTPLT64 is obsolete and treated the the same as + // GOT64. gold_assert(have_got_offset); Relocate_functions::rela64(view, got_offset, addend); break; @@ -3477,11 +3561,41 @@ Target_x86_64::Relocate::relocate( break; case elfcpp::R_X86_64_GOTPCREL: + case elfcpp::R_X86_64_GOTPCRELX: + case elfcpp::R_X86_64_REX_GOTPCRELX: { - gold_assert(have_got_offset); - typename elfcpp::Elf_types::Elf_Addr value; - value = target->got_plt_section()->address() + got_offset; - Relocate_functions::pcrela32(view, value, addend, address); + // Convert + // mov foo@GOTPCREL(%rip), %reg + // to lea foo(%rip), %reg. + // if possible. + if (rela.get_r_offset() >= 2 + && view[-2] == 0x8b + && ((gsym == NULL && !psymval->is_ifunc_symbol()) + || (gsym != NULL + && Target_x86_64::can_convert_mov_to_lea(gsym)))) + { + view[-2] = 0x8d; + Relocate_functions::pcrela32(view, object, psymval, addend, + address); + } + else + { + if (gsym != NULL) + { + gold_assert(gsym->has_got_offset(GOT_TYPE_STANDARD)); + got_offset = gsym->got_offset(GOT_TYPE_STANDARD) - target->got_size(); + } + else + { + unsigned int r_sym = elfcpp::elf_r_sym(rela.get_r_info()); + gold_assert(object->local_has_got_offset(r_sym, GOT_TYPE_STANDARD)); + got_offset = (object->local_got_offset(r_sym, GOT_TYPE_STANDARD) + - target->got_size()); + } + typename elfcpp::Elf_types::Elf_Addr value; + value = target->got_plt_section()->address() + got_offset; + Relocate_functions::pcrela32(view, value, addend, address); + } } break; @@ -3776,7 +3890,17 @@ Target_x86_64::Relocate::relocate_tls( break; case elfcpp::R_X86_64_GOTTPOFF: // Initial-exec - if (optimized_type == tls::TLSOPT_TO_LE) + if (gsym != NULL + && gsym->is_undefined() + && parameters->options().output_is_executable()) + { + Target_x86_64::Relocate::tls_ie_to_le(relinfo, relnum, + NULL, rela, + r_type, value, view, + view_size); + break; + } + else if (optimized_type == tls::TLSOPT_TO_LE) { if (tls_segment == NULL) { @@ -4097,6 +4221,8 @@ Target_x86_64::Relocate::tls_ie_to_le( // movq if (op1 == 0x4c) view[-3] = 0x49; + else if (size == 32 && op1 == 0x44) + view[-3] = 0x41; view[-2] = 0xc7; view[-1] = 0xc0 | reg; } @@ -4105,6 +4231,8 @@ Target_x86_64::Relocate::tls_ie_to_le( // Special handling for %rsp. if (op1 == 0x4c) view[-3] = 0x49; + else if (size == 32 && op1 == 0x44) + view[-3] = 0x41; view[-2] = 0x81; view[-1] = 0xc0 | reg; } @@ -4113,11 +4241,14 @@ Target_x86_64::Relocate::tls_ie_to_le( // addq if (op1 == 0x4c) view[-3] = 0x4d; + else if (size == 32 && op1 == 0x44) + view[-3] = 0x45; view[-2] = 0x8d; view[-1] = 0x80 | reg | (reg << 3); } - value -= tls_segment->memsz(); + if (tls_segment != NULL) + value -= tls_segment->memsz(); Relocate_functions::rela32(view, value, 0); } @@ -4214,6 +4345,8 @@ Target_x86_64::Relocatable_size_for_reloc::get_size_for_reloc( case elfcpp::R_X86_64_GOT64: case elfcpp::R_X86_64_GOTPCREL64: case elfcpp::R_X86_64_GOTPCREL: + case elfcpp::R_X86_64_GOTPCRELX: + case elfcpp::R_X86_64_REX_GOTPCRELX: case elfcpp::R_X86_64_GOTPLT64: return 8; @@ -4304,7 +4437,6 @@ Target_x86_64::relocate_relocs( size_t reloc_count, Output_section* output_section, typename elfcpp::Elf_types::Elf_Off offset_in_output_section, - const Relocatable_relocs* rr, unsigned char* view, typename elfcpp::Elf_types::Elf_Addr view_address, section_size_type view_size, @@ -4319,7 +4451,6 @@ Target_x86_64::relocate_relocs( reloc_count, output_section, offset_in_output_section, - rr, view, view_address, view_size, @@ -4454,35 +4585,60 @@ Target_x86_64::do_ehframe_datarel_base() const // code. We have to change the function so that it always ensures // that it has enough stack space to run some random function. +static const unsigned char cmp_insn_32[] = { 0x64, 0x3b, 0x24, 0x25 }; +static const unsigned char lea_r10_insn_32[] = { 0x44, 0x8d, 0x94, 0x24 }; +static const unsigned char lea_r11_insn_32[] = { 0x44, 0x8d, 0x9c, 0x24 }; + +static const unsigned char cmp_insn_64[] = { 0x64, 0x48, 0x3b, 0x24, 0x25 }; +static const unsigned char lea_r10_insn_64[] = { 0x4c, 0x8d, 0x94, 0x24 }; +static const unsigned char lea_r11_insn_64[] = { 0x4c, 0x8d, 0x9c, 0x24 }; + template void Target_x86_64::do_calls_non_split(Relobj* object, unsigned int shndx, section_offset_type fnoffset, section_size_type fnsize, + const unsigned char*, + size_t, unsigned char* view, section_size_type view_size, std::string* from, std::string* to) const { + const char* const cmp_insn = reinterpret_cast + (size == 32 ? cmp_insn_32 : cmp_insn_64); + const char* const lea_r10_insn = reinterpret_cast + (size == 32 ? lea_r10_insn_32 : lea_r10_insn_64); + const char* const lea_r11_insn = reinterpret_cast + (size == 32 ? lea_r11_insn_32 : lea_r11_insn_64); + + const size_t cmp_insn_len = + (size == 32 ? sizeof(cmp_insn_32) : sizeof(cmp_insn_64)); + const size_t lea_r10_insn_len = + (size == 32 ? sizeof(lea_r10_insn_32) : sizeof(lea_r10_insn_64)); + const size_t lea_r11_insn_len = + (size == 32 ? sizeof(lea_r11_insn_32) : sizeof(lea_r11_insn_64)); + const size_t nop_len = (size == 32 ? 7 : 8); + // The function starts with a comparison of the stack pointer and a // field in the TCB. This is followed by a jump. // cmp %fs:NN,%rsp - if (this->match_view(view, view_size, fnoffset, "\x64\x48\x3b\x24\x25", 5) - && fnsize > 9) + if (this->match_view(view, view_size, fnoffset, cmp_insn, cmp_insn_len) + && fnsize > nop_len + 1) { // We will call __morestack if the carry flag is set after this // comparison. We turn the comparison into an stc instruction // and some nops. view[fnoffset] = '\xf9'; - this->set_view_to_nop(view, view_size, fnoffset + 1, 8); + this->set_view_to_nop(view, view_size, fnoffset + 1, nop_len); } // lea NN(%rsp),%r10 // lea NN(%rsp),%r11 else if ((this->match_view(view, view_size, fnoffset, - "\x4c\x8d\x94\x24", 4) + lea_r10_insn, lea_r10_insn_len) || this->match_view(view, view_size, fnoffset, - "\x4c\x8d\x9c\x24", 4)) + lea_r11_insn, lea_r11_insn_len)) && fnsize > 8) { // This is loading an offset from the stack pointer for a @@ -4673,7 +4829,8 @@ const Target::Target_info Target_x86_64_nacl<64>::x86_64_nacl_info = elfcpp::SHF_X86_64_LARGE, // large_common_section_flags NULL, // attributes_section NULL, // attributes_vendor - "_start" // entry_symbol_name + "_start", // entry_symbol_name + 32, // hash_entry_size }; template<> @@ -4700,7 +4857,8 @@ const Target::Target_info Target_x86_64_nacl<32>::x86_64_nacl_info = elfcpp::SHF_X86_64_LARGE, // large_common_section_flags NULL, // attributes_section NULL, // attributes_vendor - "_start" // entry_symbol_name + "_start", // entry_symbol_name + 32, // hash_entry_size }; #define NACLMASK 0xe0 // 32-byte alignment mask.