From: Alan Modra Date: Tue, 29 Oct 2013 07:15:48 +0000 (+1030) Subject: PowerPC64 ELFv2 support for gold. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=b4f7960d5307fe4aad2126382df78f63696e96b3;p=binutils-gdb.git PowerPC64 ELFv2 support for gold. elfcpp/ * powerpc.h (EF_PPC64_ABI): New enum constant. (STO_PPC64_LOCAL_BIT, STO_PPC64_LOCAL_MASK): Likewise. (ppc64_decode_local_entry): New function. (ppc64_encode_local_entry): Likewise. gold/ * powerpc.cc (Powerpc_relobj::abiversion, set_abiversion, ppc64_local_entry_offset, ppc64_local_entry_offset, do_read_symbols): New functions. (Powerpc_relobj::e_flags_, st_other_): New vars. (Powerpc_relobj::Powerpc_relobj): Call set_abiversion. (Powerpc_dynobj::abiversion, set_abiversion): New functions. (Powerpc_relobj::e_flags_): New var. (Target_powerpc::first_plt_entry_offset, plt_entry_size): Inline and adjust for ELFv2. (Target_powerpc::abiversion, set_abiversion, stk_toc): New functions. (Powerpc_relobj::do_find_special_sections): Check no .opd in ELFv2. (Powerpc_dynobj::do_find_special_sections): Likewise. (Target_powerpc::do_define_standard_symbols): Define ".TOC.". (Target_powerpc::Branch_info::make_stub): Adjust stub destination to ELFv2 local entry. (Target_powerpc::do_relax): No thread safe barriers needed for ELFv2. (Output_data_plt_powerpc::initial_plt_entry_size_, plt_entry_size): Delete. Replace all uses with first_plt_entry_offset() and plt_entry_size(). (Output_data_plt_powerpc::Output_data_plt_powerpc): Remove reserved_size parm. Update callers. (Output_data_plt_powerpc::entry_count): Update. (Output_data_plt_powerpc::first_plt_entry_offset): Make private and use Target_powerpc::first_plt_entry_offset(). (Output_data_plt_powerpc::get_plt_entry_size): Similarly and rename to plt_entry_size. (Output_data_plt_powerpc::add_ifunc_entry, add_local_ifunc_entry): Adjust reloc for ELFv2. (glink_eh_frame_fde_64): Rename to glink_eh_frame_fde_64v1. (glink_eh_frame_fde_64v2): New. (Stub_table::plt_call_size): Support ELFv2 sizing. (Output_data_glink::add_eh_frame): Use the new FDE. (Output_data_glink::set_final_data_size): Adjust for ELFv2 glink. (Stub_table::do_write): Write ELFv2 stubs and glink. (Target_powerpc::Relocate::relocate): Replaces nop after call with ld 2,24(1) and adjust local offset destination for ELFv2. --- diff --git a/elfcpp/ChangeLog b/elfcpp/ChangeLog index 0c9c0013931..4d0a5037f76 100644 --- a/elfcpp/ChangeLog +++ b/elfcpp/ChangeLog @@ -1,3 +1,10 @@ +2013-10-30 Alan Modra + + * powerpc.h (EF_PPC64_ABI): New enum constant. + (STO_PPC64_LOCAL_BIT, STO_PPC64_LOCAL_MASK): Likewise. + (ppc64_decode_local_entry): New function. + (ppc64_encode_local_entry): Likewise. + 2013-10-30 Alan Modra * powerpc.h (R_PPC64_ADDR16_HIGH, R_PPC64_ADDR16_HIGHA, diff --git a/elfcpp/powerpc.h b/elfcpp/powerpc.h index fb4256bdafe..98354a2c7d6 100644 --- a/elfcpp/powerpc.h +++ b/elfcpp/powerpc.h @@ -1,6 +1,6 @@ // powerpc.h -- ELF definitions specific to EM_PPC and EM_PPC64 -*- C++ -*- -// Copyright 2008, 2010, 2012 Free Software Foundation, Inc. +// Copyright 2008, 2010, 2012, 2013 Free Software Foundation, Inc. // Written by David S. Miller . // This file is part of elfcpp. @@ -214,6 +214,59 @@ enum EF_PPC_RELOCATABLE_LIB = 0x00008000, // PowerPC -mrelocatable-lib flag. */ }; +// e_flags values defined for powerpc64 +enum +{ + // ABI version + // 1 for original function descriptor using ABI, + // 2 for revised ABI without function descriptors, + // 0 for unspecified or not using any features affected by the differences. + EF_PPC64_ABI = 3 +}; + +enum +{ + // The ELFv2 ABI uses three bits in the symbol st_other field of a + // function definition to specify the number of instructions between a + // function's global entry point and local entry point. + // The global entry point is used when it is necessary to set up the + // toc pointer (r2) for the function. Callers must enter the global + // entry point with r12 set to the global entry point address. On + // return from the function, r2 may have a different value to that + // which it had on entry. + // The local entry point is used when r2 is known to already be valid + // for the function. There is no requirement on r12 when using the + // local entry point, and on return r2 will contain the same value as + // at entry. + // A value of zero in these bits means that the function has a single + // entry point with no requirement on r12 or r2, and that on return r2 + // will contain the same value as at entry. + // Values of one and seven are reserved. + + STO_PPC64_LOCAL_BIT = 5, + STO_PPC64_LOCAL_MASK = 0xE0 +}; + +// 3 bit other field to bytes. +static inline unsigned int +ppc64_decode_local_entry(unsigned int other) +{ + return ((1 << other) >> 2) << 2; +} + +// bytes to field value. +static inline unsigned int +ppc64_encode_local_entry(unsigned int val) +{ + return (val >= 4 * 4 + ? (val >= 8 * 4 + ? (val >= 16 * 4 ? 6 : 5) + : 4) + : (val >= 2 * 4 + ? 3 + : (val >= 1 * 4 ? 2 : 0))); +} + } // End namespace elfcpp. #endif // !defined(ELFCPP_POWERPC_H) diff --git a/gold/ChangeLog b/gold/ChangeLog index 5607cb3eb9c..6acb64e94b5 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,43 @@ +2013-10-30 Alan Modra + + * powerpc.cc (Powerpc_relobj::abiversion, set_abiversion, + ppc64_local_entry_offset, ppc64_local_entry_offset, + do_read_symbols): New functions. + (Powerpc_relobj::e_flags_, st_other_): New vars. + (Powerpc_relobj::Powerpc_relobj): Call set_abiversion. + (Powerpc_dynobj::abiversion, set_abiversion): New functions. + (Powerpc_relobj::e_flags_): New var. + (Target_powerpc::first_plt_entry_offset, plt_entry_size): Inline + and adjust for ELFv2. + (Target_powerpc::abiversion, set_abiversion, stk_toc): New functions. + (Powerpc_relobj::do_find_special_sections): Check no .opd in ELFv2. + (Powerpc_dynobj::do_find_special_sections): Likewise. + (Target_powerpc::do_define_standard_symbols): Define ".TOC.". + (Target_powerpc::Branch_info::make_stub): Adjust stub destination + to ELFv2 local entry. + (Target_powerpc::do_relax): No thread safe barriers needed for + ELFv2. + (Output_data_plt_powerpc::initial_plt_entry_size_, + plt_entry_size): Delete. Replace all uses with + first_plt_entry_offset() and plt_entry_size(). + (Output_data_plt_powerpc::Output_data_plt_powerpc): Remove + reserved_size parm. Update callers. + (Output_data_plt_powerpc::entry_count): Update. + (Output_data_plt_powerpc::first_plt_entry_offset): Make private + and use Target_powerpc::first_plt_entry_offset(). + (Output_data_plt_powerpc::get_plt_entry_size): Similarly and + rename to plt_entry_size. + (Output_data_plt_powerpc::add_ifunc_entry, + add_local_ifunc_entry): Adjust reloc for ELFv2. + (glink_eh_frame_fde_64): Rename to glink_eh_frame_fde_64v1. + (glink_eh_frame_fde_64v2): New. + (Stub_table::plt_call_size): Support ELFv2 sizing. + (Output_data_glink::add_eh_frame): Use the new FDE. + (Output_data_glink::set_final_data_size): Adjust for ELFv2 glink. + (Stub_table::do_write): Write ELFv2 stubs and glink. + (Target_powerpc::Relocate::relocate): Replaces nop after call + with ld 2,24(1) and adjust local offset destination for ELFv2. + 2013-10-30 Alan Modra * powerpc.cc (Target_powerpc::Scan::check_non_pic): Handle new relocs. diff --git a/gold/powerpc.cc b/gold/powerpc.cc index 75fc604cf27..0d6822d03bd 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -77,12 +77,19 @@ public: const typename elfcpp::Ehdr& ehdr) : Sized_relobj_file(name, input_file, offset, ehdr), special_(0), has_small_toc_reloc_(false), opd_valid_(false), - opd_ent_(), access_from_map_(), has14_(), stub_table_() - { } + opd_ent_(), access_from_map_(), has14_(), stub_table_(), + e_flags_(ehdr.get_e_flags()), st_other_() + { + this->set_abiversion(0); + } ~Powerpc_relobj() { } + // Read the symbols then set up st_other vector. + void + do_read_symbols(Read_symbols_data*); + // The .got2 section shndx. unsigned int got2_shndx() const @@ -268,6 +275,22 @@ public: return NULL; } + int + abiversion() const + { return this->e_flags_ & elfcpp::EF_PPC64_ABI; } + + // Set ABI version for input and output + void + set_abiversion(int ver); + + unsigned int + ppc64_local_entry_offset(const Symbol* sym) const + { return elfcpp::ppc64_decode_local_entry(sym->nonvis() >> 3); } + + unsigned int + ppc64_local_entry_offset(unsigned int symndx) const + { return elfcpp::ppc64_decode_local_entry(this->st_other_[symndx] >> 5); } + private: struct Opd_ent { @@ -321,6 +344,12 @@ private: // The stub table to use for a given input section. std::vector*> stub_table_; + + // Header e_flags + elfcpp::Elf_Word e_flags_; + + // ELF st_other field for local symbols. + std::vector st_other_; }; template @@ -332,8 +361,10 @@ public: Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset, const typename elfcpp::Ehdr& ehdr) : Sized_dynobj(name, input_file, offset, ehdr), - opd_shndx_(0), opd_ent_() - { } + opd_shndx_(0), opd_ent_(), e_flags_(ehdr.get_e_flags()) + { + this->set_abiversion(0); + } ~Powerpc_dynobj() { } @@ -387,6 +418,14 @@ public: this->opd_ent_[ndx].off = value; } + int + abiversion() const + { return this->e_flags_ & elfcpp::EF_PPC64_ABI; } + + // Set ABI version for input and output. + void + set_abiversion(int ver); + private: // Used to specify extent of executable sections. struct Sec_info @@ -424,6 +463,9 @@ private: // corresponding to the address. Note that in dynamic objects, // offset is *not* relative to the section. std::vector opd_ent_; + + // Header e_flags + elfcpp::Elf_Word e_flags_; }; template @@ -679,11 +721,25 @@ class Target_powerpc : public Sized_target // Return the offset of the first non-reserved PLT entry. unsigned int - first_plt_entry_offset() const; + first_plt_entry_offset() const + { + if (size == 32) + return 0; + if (this->abiversion() >= 2) + return 16; + return 24; + } // Return the size of each PLT entry. unsigned int - plt_entry_size() const; + plt_entry_size() const + { + if (size == 32) + return 4; + if (this->abiversion() >= 2) + return 8; + return 24; + } // Add any special sections for this symbol to the gc work list. // For powerpc64, this adds the code section of a function @@ -743,6 +799,24 @@ class Target_powerpc : public Sized_target plt_thread_safe() const { return this->plt_thread_safe_; } + int + abiversion () const + { return this->processor_specific_flags() & elfcpp::EF_PPC64_ABI; } + + void + set_abiversion (int ver) + { + elfcpp::Elf_Word flags = this->processor_specific_flags(); + flags &= ~elfcpp::EF_PPC64_ABI; + flags |= ver & elfcpp::EF_PPC64_ABI; + this->set_processor_specific_flags(flags); + } + + // Offset to to save stack slot + int + stk_toc () const + { return this->abiversion() < 2 ? 40 : 24; } + private: class Track_tls @@ -1659,6 +1733,29 @@ public: } }; +// Set ABI version for input and output. + +template +void +Powerpc_relobj::set_abiversion(int ver) +{ + this->e_flags_ |= ver; + if (this->abiversion() != 0) + { + Target_powerpc* target = + static_cast*>( + parameters->sized_target()); + if (target->abiversion() == 0) + target->set_abiversion(this->abiversion()); + else if (target->abiversion() != this->abiversion()) + gold_error(_("%s: ABI version %d is not compatible " + "with ABI version %d output"), + this->name().c_str(), + this->abiversion(), target->abiversion()); + + } +} + // Stash away the index of .got2 or .opd in a relocatable object, if // such a section exists. @@ -1680,6 +1777,14 @@ Powerpc_relobj::do_find_special_sections( { unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes::shdr_size; this->special_ = ndx; + if (size == 64) + { + if (this->abiversion() == 0) + this->set_abiversion(1); + else if (this->abiversion() > 1) + gold_error(_("%s: .opd invalid in abiv%d"), + this->name().c_str(), this->abiversion()); + } } return Sized_relobj_file::do_find_special_sections(sd); } @@ -1790,6 +1895,69 @@ Powerpc_relobj::do_read_relocs(Read_relocs_data* rd) } } +// Read the symbols then set up st_other vector. + +template +void +Powerpc_relobj::do_read_symbols(Read_symbols_data* sd) +{ + Sized_relobj_file::do_read_symbols(sd); + if (size == 64) + { + const int shdr_size = elfcpp::Elf_sizes::shdr_size; + const unsigned char* const pshdrs = sd->section_headers->data(); + const unsigned int loccount = this->do_local_symbol_count(); + if (loccount != 0) + { + this->st_other_.resize(loccount); + const int sym_size = elfcpp::Elf_sizes::sym_size; + off_t locsize = loccount * sym_size; + const unsigned int symtab_shndx = this->symtab_shndx(); + const unsigned char *psymtab = pshdrs + symtab_shndx * shdr_size; + typename elfcpp::Shdr shdr(psymtab); + const unsigned char* psyms = this->get_view(shdr.get_sh_offset(), + locsize, true, false); + psyms += sym_size; + for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size) + { + elfcpp::Sym sym(psyms); + unsigned char st_other = sym.get_st_other(); + this->st_other_[i] = st_other; + if ((st_other & elfcpp::STO_PPC64_LOCAL_MASK) != 0) + { + if (this->abiversion() == 0) + this->set_abiversion(2); + else if (this->abiversion() < 2) + gold_error(_("%s: local symbol %d has invalid st_other" + " for ABI version 1"), + this->name().c_str(), i); + } + } + } + } +} + +template +void +Powerpc_dynobj::set_abiversion(int ver) +{ + this->e_flags_ |= ver; + if (this->abiversion() != 0) + { + Target_powerpc* target = + static_cast*>( + parameters->sized_target()); + if (target->abiversion() == 0) + target->set_abiversion(this->abiversion()); + else if (target->abiversion() != this->abiversion()) + gold_error(_("%s: ABI version %d is not compatible " + "with ABI version %d output"), + this->name().c_str(), + this->abiversion(), target->abiversion()); + + } +} + // Call Sized_dynobj::do_read_symbols to read the symbols then // read .opd from a dynamic object, filling in opd_ent_ vector, @@ -1821,6 +1989,12 @@ Powerpc_dynobj::do_read_symbols(Read_symbols_data* sd) if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS && (shdr.get_sh_flags() & elfcpp::SHF_ALLOC) != 0) { + if (this->abiversion() == 0) + this->set_abiversion(1); + else if (this->abiversion() > 1) + gold_error(_("%s: .opd invalid in abiv%d"), + this->name().c_str(), this->abiversion()); + this->opd_shndx_ = (s - pshdrs) / shdr_size; this->opd_address_ = shdr.get_sh_addr(); opd_size = convert_to_section_size_type(shdr.get_sh_size()); @@ -1937,6 +2111,26 @@ Target_powerpc::do_define_standard_symbols( 0, false, false); } } + else + { + // Define .TOC. as for 32-bit _GLOBAL_OFFSET_TABLE_ + Symbol *gotsym = symtab->lookup(".TOC.", NULL); + if (gotsym != NULL && gotsym->is_undefined()) + { + Target_powerpc* target = + static_cast*>( + parameters->sized_target()); + Output_data_got_powerpc* got + = target->got_section(symtab, layout); + symtab->define_in_output_data(".TOC.", NULL, + Symbol_table::PREDEFINED, + got, 0x8000, 0, + elfcpp::STT_OBJECT, + elfcpp::STB_LOCAL, + elfcpp::STV_HIDDEN, 0, + false, false); + } + } } // Set up PowerPC target specific relobj. @@ -2446,7 +2640,7 @@ Target_powerpc::Branch_info::make_stub( } else { - unsigned int max_branch_offset; + unsigned long max_branch_offset; if (this->r_type_ == elfcpp::R_POWERPC_REL14 || this->r_type_ == elfcpp::R_POWERPC_REL14_BRTAKEN || this->r_type_ == elfcpp::R_POWERPC_REL14_BRNTAKEN) @@ -2489,6 +2683,7 @@ Target_powerpc::Branch_info::make_stub( to = symtab->compute_final_value(gsym, &status); if (status != Symbol_table::CFVS_OK) return; + to += this->object_->ppc64_local_entry_offset(gsym); } else { @@ -2503,6 +2698,7 @@ Target_powerpc::Branch_info::make_stub( || !symval.has_output_value()) return; to = symval.value(this->object_, 0); + to += this->object_->ppc64_local_entry_offset(this->r_sym_); } to += this->addend_; if (stub_table == NULL) @@ -2545,8 +2741,12 @@ Target_powerpc::do_relax(int pass, unsigned int prev_brlt_size = 0; if (pass == 1) { - bool thread_safe = parameters->options().plt_thread_safe(); - if (size == 64 && !parameters->options().user_set_plt_thread_safe()) + bool thread_safe + = this->abiversion() < 2 && parameters->options().plt_thread_safe(); + if (size == 64 + && this->abiversion() < 2 + && !thread_safe + && !parameters->options().user_set_plt_thread_safe()) { static const char* const thread_starter[] = { @@ -2758,12 +2958,10 @@ class Output_data_plt_powerpc : public Output_section_data_build Output_data_plt_powerpc(Target_powerpc* targ, Reloc_section* plt_rel, - unsigned int reserved_size, const char* name) : Output_section_data_build(size == 32 ? 4 : 8), rel_(plt_rel), targ_(targ), - initial_plt_entry_size_(reserved_size), name_(name) { } @@ -2790,20 +2988,10 @@ class Output_data_plt_powerpc : public Output_section_data_build { if (this->current_data_size() == 0) return 0; - return ((this->current_data_size() - this->initial_plt_entry_size_) - / plt_entry_size); + return ((this->current_data_size() - this->first_plt_entry_offset()) + / this->plt_entry_size()); } - // Return the offset of the first non-reserved PLT entry. - unsigned int - first_plt_entry_offset() - { return this->initial_plt_entry_size_; } - - // Return the size of a PLT entry. - static unsigned int - get_plt_entry_size() - { return plt_entry_size; } - protected: void do_adjust_output_section(Output_section* os) @@ -2817,8 +3005,22 @@ class Output_data_plt_powerpc : public Output_section_data_build { mapfile->print_output_data(this, this->name_); } private: - // The size of an entry in the PLT. - static const int plt_entry_size = size == 32 ? 4 : 24; + // Return the offset of the first non-reserved PLT entry. + unsigned int + first_plt_entry_offset() const + { + // IPLT has no reserved entry. + if (this->name_[3] == 'I') + return 0; + return this->targ_->first_plt_entry_offset(); + } + + // Return the size of each PLT entry. + unsigned int + plt_entry_size() const + { + return this->targ_->plt_entry_size(); + } // Write out the PLT data. void @@ -2828,8 +3030,6 @@ class Output_data_plt_powerpc : public Output_section_data_build Reloc_section* rel_; // Allows access to .glink for do_write. Target_powerpc* targ_; - // The size of the first reserved entry. - int initial_plt_entry_size_; // What to report in map file. const char *name_; }; @@ -2849,7 +3049,7 @@ Output_data_plt_powerpc::add_entry(Symbol* gsym) gsym->set_needs_dynsym_entry(); unsigned int dynrel = elfcpp::R_POWERPC_JMP_SLOT; this->rel_->add_global(gsym, dynrel, this, off, 0); - off += plt_entry_size; + off += this->plt_entry_size(); this->set_current_data_size(off); } } @@ -2865,10 +3065,10 @@ Output_data_plt_powerpc::add_ifunc_entry(Symbol* gsym) section_size_type off = this->current_data_size(); gsym->set_plt_offset(off); unsigned int dynrel = elfcpp::R_POWERPC_IRELATIVE; - if (size == 64) + if (size == 64 && this->targ_->abiversion() < 2) dynrel = elfcpp::R_PPC64_JMP_IREL; this->rel_->add_symbolless_global_addend(gsym, dynrel, this, off, 0); - off += plt_entry_size; + off += this->plt_entry_size(); this->set_current_data_size(off); } } @@ -2886,11 +3086,11 @@ Output_data_plt_powerpc::add_local_ifunc_entry( section_size_type off = this->current_data_size(); relobj->set_local_plt_offset(local_sym_index, off); unsigned int dynrel = elfcpp::R_POWERPC_IRELATIVE; - if (size == 64) + if (size == 64 && this->targ_->abiversion() < 2) dynrel = elfcpp::R_PPC64_JMP_IREL; this->rel_->add_symbolless_local_addend(relobj, local_sym_index, dynrel, this, off, 0); - off += plt_entry_size; + off += this->plt_entry_size(); this->set_current_data_size(off); } } @@ -2900,50 +3100,50 @@ static const uint32_t add_2_2_11 = 0x7c425a14; static const uint32_t add_3_3_2 = 0x7c631214; static const uint32_t add_3_3_13 = 0x7c636a14; static const uint32_t add_11_0_11 = 0x7d605a14; -static const uint32_t add_12_2_11 = 0x7d825a14; -static const uint32_t add_12_12_11 = 0x7d8c5a14; -static const uint32_t addi_11_11 = 0x396b0000; -static const uint32_t addi_12_12 = 0x398c0000; +static const uint32_t add_11_2_11 = 0x7d625a14; +static const uint32_t add_11_11_2 = 0x7d6b1214; +static const uint32_t addi_0_12 = 0x380c0000; static const uint32_t addi_2_2 = 0x38420000; -static const uint32_t addi_3_2 = 0x38620000; static const uint32_t addi_3_3 = 0x38630000; +static const uint32_t addi_11_11 = 0x396b0000; +static const uint32_t addi_12_12 = 0x398c0000; static const uint32_t addis_0_2 = 0x3c020000; static const uint32_t addis_0_13 = 0x3c0d0000; +static const uint32_t addis_3_2 = 0x3c620000; +static const uint32_t addis_3_13 = 0x3c6d0000; +static const uint32_t addis_11_2 = 0x3d620000; static const uint32_t addis_11_11 = 0x3d6b0000; static const uint32_t addis_11_30 = 0x3d7e0000; static const uint32_t addis_12_12 = 0x3d8c0000; -static const uint32_t addis_12_2 = 0x3d820000; -static const uint32_t addis_3_2 = 0x3c620000; -static const uint32_t addis_3_13 = 0x3c6d0000; static const uint32_t b = 0x48000000; static const uint32_t bcl_20_31 = 0x429f0005; static const uint32_t bctr = 0x4e800420; static const uint32_t blr = 0x4e800020; -static const uint32_t blrl = 0x4e800021; static const uint32_t bnectr_p4 = 0x4ce20420; static const uint32_t cmpldi_2_0 = 0x28220000; static const uint32_t cror_15_15_15 = 0x4def7b82; static const uint32_t cror_31_31_31 = 0x4ffffb82; static const uint32_t ld_0_1 = 0xe8010000; static const uint32_t ld_0_12 = 0xe80c0000; -static const uint32_t ld_11_12 = 0xe96c0000; -static const uint32_t ld_11_2 = 0xe9620000; static const uint32_t ld_2_1 = 0xe8410000; -static const uint32_t ld_2_11 = 0xe84b0000; -static const uint32_t ld_2_12 = 0xe84c0000; static const uint32_t ld_2_2 = 0xe8420000; +static const uint32_t ld_2_11 = 0xe84b0000; +static const uint32_t ld_11_2 = 0xe9620000; +static const uint32_t ld_11_11 = 0xe96b0000; +static const uint32_t ld_12_2 = 0xe9820000; +static const uint32_t ld_12_11 = 0xe98b0000; static const uint32_t lfd_0_1 = 0xc8010000; static const uint32_t li_0_0 = 0x38000000; static const uint32_t li_12_0 = 0x39800000; static const uint32_t lis_0_0 = 0x3c000000; static const uint32_t lis_11 = 0x3d600000; static const uint32_t lis_12 = 0x3d800000; +static const uint32_t lvx_0_12_0 = 0x7c0c00ce; static const uint32_t lwz_0_12 = 0x800c0000; static const uint32_t lwz_11_11 = 0x816b0000; static const uint32_t lwz_11_30 = 0x817e0000; static const uint32_t lwz_12_12 = 0x818c0000; static const uint32_t lwzu_0_12 = 0x840c0000; -static const uint32_t lvx_0_12_0 = 0x7c0c00ce; static const uint32_t mflr_0 = 0x7c0802a6; static const uint32_t mflr_11 = 0x7d6802a6; static const uint32_t mflr_12 = 0x7d8802a6; @@ -2954,13 +3154,16 @@ static const uint32_t mtlr_0 = 0x7c0803a6; static const uint32_t mtlr_12 = 0x7d8803a6; static const uint32_t nop = 0x60000000; static const uint32_t ori_0_0_0 = 0x60000000; +static const uint32_t srdi_0_0_2 = 0x7800f082; static const uint32_t std_0_1 = 0xf8010000; static const uint32_t std_0_12 = 0xf80c0000; static const uint32_t std_2_1 = 0xf8410000; static const uint32_t stfd_0_1 = 0xd8010000; static const uint32_t stvx_0_12_0 = 0x7c0c01ce; static const uint32_t sub_11_11_12 = 0x7d6c5850; -static const uint32_t xor_11_11_11 = 0x7d6b5a78; +static const uint32_t sub_12_12_11 = 0x7d8b6050; +static const uint32_t xor_2_12_12 = 0x7d826278; +static const uint32_t xor_11_12_12 = 0x7d8b6278; // Write out the PLT. @@ -3019,7 +3222,6 @@ Target_powerpc::make_plt_section(Symbol_table* symtab, ORDER_DYNAMIC_PLT_RELOCS, false); this->plt_ = new Output_data_plt_powerpc(this, plt_rel, - size == 32 ? 0 : 24, "** PLT"); layout->add_output_section_data(".plt", (size == 32 @@ -3049,7 +3251,7 @@ Target_powerpc::make_iplt_section(Symbol_table* symtab, this->rela_dyn_->output_section()->add_output_section_data(iplt_rel); this->iplt_ = new Output_data_plt_powerpc(this, iplt_rel, - 0, "** IPLT"); + "** IPLT"); this->plt_->output_section()->add_output_section_data(this->iplt_); } } @@ -3225,8 +3427,8 @@ const unsigned char Eh_cie::eh_frame_cie[] = elfcpp::DW_CFA_def_cfa, 1, 0 // def_cfa: r1 offset 0. }; -// Describe __glink_PLTresolve use of LR, 64-bit version. -static const unsigned char glink_eh_frame_fde_64[] = +// Describe __glink_PLTresolve use of LR, 64-bit version ABIv1. +static const unsigned char glink_eh_frame_fde_64v1[] = { 0, 0, 0, 0, // Replaced with offset to .glink. 0, 0, 0, 0, // Replaced with size of .glink. @@ -3237,6 +3439,18 @@ static const unsigned char glink_eh_frame_fde_64[] = elfcpp::DW_CFA_restore_extended, 65 }; +// Describe __glink_PLTresolve use of LR, 64-bit version ABIv2. +static const unsigned char glink_eh_frame_fde_64v2[] = +{ + 0, 0, 0, 0, // Replaced with offset to .glink. + 0, 0, 0, 0, // Replaced with size of .glink. + 0, // Augmentation size. + elfcpp::DW_CFA_advance_loc + 1, + elfcpp::DW_CFA_register, 65, 0, + elfcpp::DW_CFA_advance_loc + 4, + elfcpp::DW_CFA_restore_extended, 65 +}; + // Describe __glink_PLTresolve use of LR, 32-bit version. static const unsigned char glink_eh_frame_fde_32[] = { @@ -3493,13 +3707,16 @@ class Stub_table : public Output_relaxed_input_section *>(p->first.object_); got_addr += ppcobj->toc_base_offset(); Address off = plt_addr - got_addr; - bool static_chain = parameters->options().plt_static_chain(); - bool thread_safe = this->targ_->plt_thread_safe(); - unsigned int bytes = (4 * 5 - + 4 * static_chain - + 8 * thread_safe - + 4 * (ha(off) != 0) - + 4 * (ha(off + 8 + 8 * static_chain) != ha(off))); + unsigned int bytes = 4 * 4 + 4 * (ha(off) != 0); + if (this->targ_->abiversion() < 2) + { + bool static_chain = parameters->options().plt_static_chain(); + bool thread_safe = this->targ_->plt_thread_safe(); + bytes += (4 + + 4 * static_chain + + 8 * thread_safe + + 4 * (ha(off + 8 + 8 * static_chain) != ha(off))); + } unsigned int align = 1 << parameters->options().plt_align(); if (align > 1) bytes = (bytes + align - 1) & -align; @@ -3805,11 +4022,20 @@ class Output_data_glink : public Output_section_data return; if (size == 64) - layout->add_eh_frame_for_plt(this, - Eh_cie<64>::eh_frame_cie, - sizeof (Eh_cie<64>::eh_frame_cie), - glink_eh_frame_fde_64, - sizeof (glink_eh_frame_fde_64)); + { + if (this->targ_->abiversion() < 2) + layout->add_eh_frame_for_plt(this, + Eh_cie<64>::eh_frame_cie, + sizeof (Eh_cie<64>::eh_frame_cie), + glink_eh_frame_fde_64v1, + sizeof (glink_eh_frame_fde_64v1)); + else + layout->add_eh_frame_for_plt(this, + Eh_cie<64>::eh_frame_cie, + sizeof (Eh_cie<64>::eh_frame_cie), + glink_eh_frame_fde_64v2, + sizeof (glink_eh_frame_fde_64v2)); + } else { // 32-bit .glink can use the default since the CIE return @@ -3869,9 +4095,13 @@ Output_data_glink::set_final_data_size() total += this->pltresolve_size; // space for branch table - total += 8 * count; - if (count > 0x8000) - total += 4 * (count - 0x8000); + total += 4 * count; + if (this->targ_->abiversion() < 2) + { + total += 4 * count; + if (count > 0x8000) + total += 4 * (count - 0x8000); + } } } @@ -3934,8 +4164,11 @@ Stub_table::do_write(Output_file* of) cs->first.object_->name().c_str(), cs->first.sym_->demangled_name().c_str()); - bool static_chain = parameters->options().plt_static_chain(); - bool thread_safe = this->targ_->plt_thread_safe(); + bool plt_load_toc = this->targ_->abiversion() < 2; + bool static_chain + = plt_load_toc && parameters->options().plt_static_chain(); + bool thread_safe + = plt_load_toc && this->targ_->plt_thread_safe(); bool use_fake_dep = false; Address cmp_branch_off = 0; if (thread_safe) @@ -3962,47 +4195,78 @@ Stub_table::do_write(Output_file* of) p = oview + cs->second; if (ha(off) != 0) { - write_insn(p, std_2_1 + 40), p += 4; - write_insn(p, addis_12_2 + ha(off)), p += 4; - write_insn(p, ld_11_12 + l(off)), p += 4; - if (ha(off + 8 + 8 * static_chain) != ha(off)) + write_insn(p, std_2_1 + this->targ_->stk_toc()); + p += 4; + write_insn(p, addis_11_2 + ha(off)); + p += 4; + write_insn(p, ld_12_11 + l(off)); + p += 4; + if (plt_load_toc + && ha(off + 8 + 8 * static_chain) != ha(off)) { - write_insn(p, addi_12_12 + l(off)), p += 4; + write_insn(p, addi_11_11 + l(off)); + p += 4; off = 0; } - write_insn(p, mtctr_11), p += 4; - if (use_fake_dep) + write_insn(p, mtctr_12); + p += 4; + if (plt_load_toc) { - write_insn(p, xor_11_11_11), p += 4; - write_insn(p, add_12_12_11), p += 4; + if (use_fake_dep) + { + write_insn(p, xor_2_12_12); + p += 4; + write_insn(p, add_11_11_2); + p += 4; + } + write_insn(p, ld_2_11 + l(off + 8)); + p += 4; + if (static_chain) + { + write_insn(p, ld_11_11 + l(off + 16)); + p += 4; + } } - write_insn(p, ld_2_12 + l(off + 8)), p += 4; - if (static_chain) - write_insn(p, ld_11_12 + l(off + 16)), p += 4; } else { - write_insn(p, std_2_1 + 40), p += 4; - write_insn(p, ld_11_2 + l(off)), p += 4; - if (ha(off + 8 + 8 * static_chain) != ha(off)) + write_insn(p, std_2_1 + this->targ_->stk_toc()); + p += 4; + write_insn(p, ld_12_2 + l(off)); + p += 4; + if (plt_load_toc + && ha(off + 8 + 8 * static_chain) != ha(off)) { - write_insn(p, addi_2_2 + l(off)), p += 4; + write_insn(p, addi_2_2 + l(off)); + p += 4; off = 0; } - write_insn(p, mtctr_11), p += 4; - if (use_fake_dep) + write_insn(p, mtctr_12); + p += 4; + if (plt_load_toc) { - write_insn(p, xor_11_11_11), p += 4; - write_insn(p, add_2_2_11), p += 4; + if (use_fake_dep) + { + write_insn(p, xor_11_12_12); + p += 4; + write_insn(p, add_2_2_11); + p += 4; + } + if (static_chain) + { + write_insn(p, ld_11_2 + l(off + 16)); + p += 4; + } + write_insn(p, ld_2_2 + l(off + 8)); + p += 4; } - if (static_chain) - write_insn(p, ld_11_2 + l(off + 16)), p += 4; - write_insn(p, ld_2_2 + l(off + 8)), p += 4; } if (thread_safe && !use_fake_dep) { - write_insn(p, cmpldi_2_0), p += 4; - write_insn(p, bnectr_p4), p += 4; + write_insn(p, cmpldi_2_0); + p += 4; + write_insn(p, bnectr_p4); + p += 4; write_insn(p, b | (cmp_branch_off & 0x3fffffc)); } else @@ -4031,14 +4295,14 @@ Stub_table::do_write(Output_file* of) Address brltoff = brlt_addr - got_addr; if (ha(brltoff) == 0) { - write_insn(p, ld_11_2 + l(brltoff)), p += 4; + write_insn(p, ld_12_2 + l(brltoff)), p += 4; } else { - write_insn(p, addis_12_2 + ha(brltoff)), p += 4; - write_insn(p, ld_11_12 + l(brltoff)), p += 4; + write_insn(p, addis_11_2 + ha(brltoff)), p += 4; + write_insn(p, ld_12_11 + l(brltoff)), p += 4; } - write_insn(p, mtctr_11), p += 4; + write_insn(p, mtctr_12), p += 4; write_insn(p, bctr); } } @@ -4180,16 +4444,34 @@ Output_data_glink::do_write(Output_file* of) elfcpp::Swap<64, big_endian>::writeval(p, pltoff), p += 8; - write_insn(p, mflr_12), p += 4; - write_insn(p, bcl_20_31), p += 4; - write_insn(p, mflr_11), p += 4; - write_insn(p, ld_2_11 + l(-16)), p += 4; - write_insn(p, mtlr_12), p += 4; - write_insn(p, add_12_2_11), p += 4; - write_insn(p, ld_11_12 + 0), p += 4; - write_insn(p, ld_2_12 + 8), p += 4; - write_insn(p, mtctr_11), p += 4; - write_insn(p, ld_11_12 + 16), p += 4; + if (this->targ_->abiversion() < 2) + { + write_insn(p, mflr_12), p += 4; + write_insn(p, bcl_20_31), p += 4; + write_insn(p, mflr_11), p += 4; + write_insn(p, ld_2_11 + l(-16)), p += 4; + write_insn(p, mtlr_12), p += 4; + write_insn(p, add_11_2_11), p += 4; + write_insn(p, ld_12_11 + 0), p += 4; + write_insn(p, ld_2_11 + 8), p += 4; + write_insn(p, mtctr_12), p += 4; + write_insn(p, ld_11_11 + 16), p += 4; + } + else + { + write_insn(p, mflr_0), p += 4; + write_insn(p, bcl_20_31), p += 4; + write_insn(p, mflr_11), p += 4; + write_insn(p, ld_2_11 + l(-16)), p += 4; + write_insn(p, mtlr_0), p += 4; + write_insn(p, sub_12_12_11), p += 4; + write_insn(p, add_11_2_11), p += 4; + write_insn(p, addi_0_12 + l(-48)), p += 4; + write_insn(p, ld_12_11 + 0), p += 4; + write_insn(p, srdi_0_0_2), p += 4; + write_insn(p, mtctr_12), p += 4; + write_insn(p, ld_11_11 + 8), p += 4; + } write_insn(p, bctr), p += 4; while (p < oview + this->pltresolve_size) write_insn(p, nop), p += 4; @@ -4198,14 +4480,17 @@ Output_data_glink::do_write(Output_file* of) uint32_t indx = 0; while (p < oview + oview_size) { - if (indx < 0x8000) + if (this->targ_->abiversion() < 2) { - write_insn(p, li_0_0 + indx), p += 4; - } - else - { - write_insn(p, lis_0_0 + hi(indx)), p += 4; - write_insn(p, ori_0_0_0 + l(indx)), p += 4; + if (indx < 0x8000) + { + write_insn(p, li_0_0 + indx), p += 4; + } + else + { + write_insn(p, lis_0_0 + hi(indx)), p += 4; + write_insn(p, ori_0_0_0 + l(indx)), p += 4; + } } uint32_t branch_off = 8 - (p - oview); write_insn(p, b + (branch_off & 0x3fffffc)), p += 4; @@ -4690,24 +4975,6 @@ Target_powerpc::plt_entry_count() const return this->plt_->entry_count(); } -// Return the offset of the first non-reserved PLT entry. - -template -unsigned int -Target_powerpc::first_plt_entry_offset() const -{ - return this->plt_->first_plt_entry_offset(); -} - -// Return the size of each PLT entry. - -template -unsigned int -Target_powerpc::plt_entry_size() const -{ - return Output_data_plt_powerpc::get_plt_entry_size(); -} - // Create a GOT entry for local dynamic __tls_get_addr calls. template @@ -6403,7 +6670,8 @@ Target_powerpc::Relocate::relocate( && (insn2 == nop || insn2 == cror_15_15_15 || insn2 == cror_31_31_31)) { - elfcpp::Swap<32, big_endian>::writeval(wv + 1, ld_2_1 + 40); + elfcpp::Swap<32, big_endian>:: + writeval(wv + 1, ld_2_1 + target->stk_toc()); can_plt_call = true; } } @@ -6695,6 +6963,10 @@ Target_powerpc::Relocate::relocate( if (r_type != elfcpp::R_PPC_PLTREL24) addend = rela.get_r_addend(); value = psymval->value(object, addend); + if (gsym != NULL) + value += object->ppc64_local_entry_offset(gsym); + else + value += object->ppc64_local_entry_offset(r_sym); if (size == 64 && is_branch_reloc(r_type)) value = target->symval_for_branch(relinfo->symtab, value, gsym, object, &dest_shndx);