| // powerpc.cc -- powerpc target support for gold. |
| |
| // Copyright (C) 2008-2024 Free Software Foundation, Inc. |
| // Written by David S. Miller <davem@davemloft.net> |
| // and David Edelsohn <edelsohn@gnu.org> |
| |
| // This file is part of gold. |
| |
| // This program is free software; you can redistribute it and/or modify |
| // it under the terms of the GNU General Public License as published by |
| // the Free Software Foundation; either version 3 of the License, or |
| // (at your option) any later version. |
| |
| // This program is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| |
| // You should have received a copy of the GNU General Public License |
| // along with this program; if not, write to the Free Software |
| // Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, |
| // MA 02110-1301, USA. |
| |
| #include "gold.h" |
| |
| #include <set> |
| #include <algorithm> |
| #include "elfcpp.h" |
| #include "dwarf.h" |
| #include "parameters.h" |
| #include "reloc.h" |
| #include "powerpc.h" |
| #include "object.h" |
| #include "symtab.h" |
| #include "layout.h" |
| #include "output.h" |
| #include "copy-relocs.h" |
| #include "target.h" |
| #include "target-reloc.h" |
| #include "target-select.h" |
| #include "tls.h" |
| #include "errors.h" |
| #include "gc.h" |
| #include "attributes.h" |
| |
| namespace |
| { |
| |
| using namespace gold; |
| |
| template<int size, bool big_endian> |
| class Output_data_plt_powerpc; |
| |
| template<int size, bool big_endian> |
| class Output_data_brlt_powerpc; |
| |
| template<int size, bool big_endian> |
| class Output_data_got_powerpc; |
| |
| template<int size, bool big_endian> |
| class Output_data_glink; |
| |
| template<int size, bool big_endian> |
| class Stub_table; |
| |
| template<int size, bool big_endian> |
| class Output_data_save_res; |
| |
| template<int size, bool big_endian> |
| class Target_powerpc; |
| |
| struct Stub_table_owner |
| { |
| Stub_table_owner() |
| : output_section(NULL), owner(NULL) |
| { } |
| |
| Output_section* output_section; |
| const Output_section::Input_section* owner; |
| }; |
| |
| template<int size> |
| inline bool is_branch_reloc(unsigned int); |
| |
| template<int size> |
| inline bool is_plt16_reloc(unsigned int); |
| |
| // Counter incremented on every Powerpc_relobj constructed. |
| static uint32_t object_id = 0; |
| |
| template<int size, bool big_endian> |
| class Powerpc_relobj : public Sized_relobj_file<size, big_endian> |
| { |
| public: |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; |
| typedef Unordered_set<Section_id, Section_id_hash> Section_refs; |
| typedef Unordered_map<Address, Section_refs> Access_from; |
| |
| Powerpc_relobj(const std::string& name, Input_file* input_file, off_t offset, |
| const typename elfcpp::Ehdr<size, big_endian>& ehdr) |
| : Sized_relobj_file<size, big_endian>(name, input_file, offset, ehdr), |
| uniq_(object_id++), special_(0), relatoc_(0), toc_(0), |
| has_small_toc_reloc_(false), opd_valid_(false), |
| e_flags_(ehdr.get_e_flags()), no_toc_opt_(), opd_ent_(), |
| access_from_map_(), has14_(), stub_table_index_(), st_other_(), |
| attributes_section_data_(NULL) |
| { |
| this->set_abiversion(0); |
| } |
| |
| ~Powerpc_relobj() |
| { delete this->attributes_section_data_; } |
| |
| // Read the symbols then set up st_other vector. |
| void |
| do_read_symbols(Read_symbols_data*); |
| |
| // Arrange to always relocate .toc first. |
| virtual void |
| do_relocate_sections( |
| const Symbol_table* symtab, const Layout* layout, |
| const unsigned char* pshdrs, Output_file* of, |
| typename Sized_relobj_file<size, big_endian>::Views* pviews); |
| |
| // The .toc section index. |
| unsigned int |
| toc_shndx() const |
| { |
| return this->toc_; |
| } |
| |
| // Mark .toc entry at OFF as not optimizable. |
| void |
| set_no_toc_opt(Address off) |
| { |
| if (this->no_toc_opt_.empty()) |
| this->no_toc_opt_.resize(this->section_size(this->toc_shndx()) |
| / (size / 8)); |
| off /= size / 8; |
| if (off < this->no_toc_opt_.size()) |
| this->no_toc_opt_[off] = true; |
| } |
| |
| // Mark the entire .toc as not optimizable. |
| void |
| set_no_toc_opt() |
| { |
| this->no_toc_opt_.resize(1); |
| this->no_toc_opt_[0] = true; |
| } |
| |
| // Return true if code using the .toc entry at OFF should not be edited. |
| bool |
| no_toc_opt(Address off) const |
| { |
| if (this->no_toc_opt_.empty()) |
| return false; |
| off /= size / 8; |
| if (off >= this->no_toc_opt_.size()) |
| return true; |
| return this->no_toc_opt_[off]; |
| } |
| |
| // The .got2 section shndx. |
| unsigned int |
| got2_shndx() const |
| { |
| if (size == 32) |
| return this->special_; |
| else |
| return 0; |
| } |
| |
| // The .opd section shndx. |
| unsigned int |
| opd_shndx() const |
| { |
| if (size == 32) |
| return 0; |
| else |
| return this->special_; |
| } |
| |
| // Init OPD entry arrays. |
| void |
| init_opd(size_t opd_size) |
| { |
| size_t count = this->opd_ent_ndx(opd_size); |
| this->opd_ent_.resize(count); |
| } |
| |
| // Return section and offset of function entry for .opd + R_OFF. |
| unsigned int |
| get_opd_ent(Address r_off, Address* value = NULL) const |
| { |
| size_t ndx = this->opd_ent_ndx(r_off); |
| gold_assert(ndx < this->opd_ent_.size()); |
| gold_assert(this->opd_ent_[ndx].shndx != 0); |
| if (value != NULL) |
| *value = this->opd_ent_[ndx].off; |
| return this->opd_ent_[ndx].shndx; |
| } |
| |
| // Set section and offset of function entry for .opd + R_OFF. |
| void |
| set_opd_ent(Address r_off, unsigned int shndx, Address value) |
| { |
| size_t ndx = this->opd_ent_ndx(r_off); |
| gold_assert(ndx < this->opd_ent_.size()); |
| this->opd_ent_[ndx].shndx = shndx; |
| this->opd_ent_[ndx].off = value; |
| } |
| |
| // Return discard flag for .opd + R_OFF. |
| bool |
| get_opd_discard(Address r_off) const |
| { |
| size_t ndx = this->opd_ent_ndx(r_off); |
| gold_assert(ndx < this->opd_ent_.size()); |
| return this->opd_ent_[ndx].discard; |
| } |
| |
| // Set discard flag for .opd + R_OFF. |
| void |
| set_opd_discard(Address r_off) |
| { |
| size_t ndx = this->opd_ent_ndx(r_off); |
| gold_assert(ndx < this->opd_ent_.size()); |
| this->opd_ent_[ndx].discard = true; |
| } |
| |
| bool |
| opd_valid() const |
| { return this->opd_valid_; } |
| |
| void |
| set_opd_valid() |
| { this->opd_valid_ = true; } |
| |
| // Examine .rela.opd to build info about function entry points. |
| void |
| scan_opd_relocs(size_t reloc_count, |
| const unsigned char* prelocs, |
| const unsigned char* plocal_syms); |
| |
| // Returns true if a code sequence loading a TOC entry can be |
| // converted into code calculating a TOC pointer relative offset. |
| bool |
| make_toc_relative(Target_powerpc<size, big_endian>* target, |
| Address* value); |
| |
| bool |
| make_got_relative(Target_powerpc<size, big_endian>* target, |
| const Symbol_value<size>* psymval, |
| Address addend, |
| Address* value); |
| |
| // Perform the Sized_relobj_file method, then set up opd info from |
| // .opd relocs. |
| void |
| do_read_relocs(Read_relocs_data*); |
| |
| bool |
| do_find_special_sections(Read_symbols_data* sd); |
| |
| // Adjust this local symbol value. Return false if the symbol |
| // should be discarded from the output file. |
| bool |
| do_adjust_local_symbol(Symbol_value<size>* lv) const |
| { |
| if (size == 64 && this->opd_shndx() != 0) |
| { |
| bool is_ordinary; |
| if (lv->input_shndx(&is_ordinary) != this->opd_shndx()) |
| return true; |
| if (this->get_opd_discard(lv->input_value())) |
| return false; |
| } |
| return true; |
| } |
| |
| Access_from* |
| access_from_map() |
| { return &this->access_from_map_; } |
| |
| // Add a reference from SRC_OBJ, SRC_INDX to this object's .opd |
| // section at DST_OFF. |
| void |
| add_reference(Relobj* src_obj, |
| unsigned int src_indx, |
| typename elfcpp::Elf_types<size>::Elf_Addr dst_off) |
| { |
| Section_id src_id(src_obj, src_indx); |
| this->access_from_map_[dst_off].insert(src_id); |
| } |
| |
| // Add a reference to the code section specified by the .opd entry |
| // at DST_OFF |
| void |
| add_gc_mark(typename elfcpp::Elf_types<size>::Elf_Addr dst_off) |
| { |
| size_t ndx = this->opd_ent_ndx(dst_off); |
| if (ndx >= this->opd_ent_.size()) |
| this->opd_ent_.resize(ndx + 1); |
| this->opd_ent_[ndx].gc_mark = true; |
| } |
| |
| void |
| process_gc_mark(Symbol_table* symtab) |
| { |
| for (size_t i = 0; i < this->opd_ent_.size(); i++) |
| if (this->opd_ent_[i].gc_mark) |
| { |
| unsigned int shndx = this->opd_ent_[i].shndx; |
| symtab->gc()->worklist().push_back(Section_id(this, shndx)); |
| } |
| } |
| |
| void |
| set_has_small_toc_reloc() |
| { has_small_toc_reloc_ = true; } |
| |
| bool |
| has_small_toc_reloc() const |
| { return has_small_toc_reloc_; } |
| |
| void |
| set_has_14bit_branch(unsigned int shndx) |
| { |
| if (shndx >= this->has14_.size()) |
| this->has14_.resize(shndx + 1); |
| this->has14_[shndx] = true; |
| } |
| |
| bool |
| has_14bit_branch(unsigned int shndx) const |
| { return shndx < this->has14_.size() && this->has14_[shndx]; } |
| |
| void |
| set_stub_table(unsigned int shndx, unsigned int stub_index) |
| { |
| if (shndx >= this->stub_table_index_.size()) |
| this->stub_table_index_.resize(shndx + 1, -1); |
| this->stub_table_index_[shndx] = stub_index; |
| } |
| |
| Stub_table<size, big_endian>* |
| stub_table(unsigned int shndx) |
| { |
| if (shndx < this->stub_table_index_.size()) |
| { |
| Target_powerpc<size, big_endian>* target |
| = static_cast<Target_powerpc<size, big_endian>*>( |
| parameters->sized_target<size, big_endian>()); |
| unsigned int indx = this->stub_table_index_[shndx]; |
| if (indx < target->stub_tables().size()) |
| return target->stub_tables()[indx]; |
| } |
| return NULL; |
| } |
| |
| void |
| clear_stub_table() |
| { |
| this->stub_table_index_.clear(); |
| } |
| |
| uint32_t |
| uniq() const |
| { return this->uniq_; } |
| |
| 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 |
| st_other (unsigned int symndx) const |
| { |
| return this->st_other_[symndx]; |
| } |
| |
| 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); } |
| |
| bool |
| ppc64_needs_toc(const Symbol* sym) const |
| { return sym->nonvis() > 1 << 3; } |
| |
| bool |
| ppc64_needs_toc(unsigned int symndx) const |
| { return this->st_other_[symndx] > 1 << 5; } |
| |
| // The contents of the .gnu.attributes section if there is one. |
| const Attributes_section_data* |
| attributes_section_data() const |
| { return this->attributes_section_data_; } |
| |
| private: |
| struct Opd_ent |
| { |
| unsigned int shndx; |
| bool discard : 1; |
| bool gc_mark : 1; |
| Address off; |
| }; |
| |
| // Return index into opd_ent_ array for .opd entry at OFF. |
| // .opd entries are 24 bytes long, but they can be spaced 16 bytes |
| // apart when the language doesn't use the last 8-byte word, the |
| // environment pointer. Thus dividing the entry section offset by |
| // 16 will give an index into opd_ent_ that works for either layout |
| // of .opd. (It leaves some elements of the vector unused when .opd |
| // entries are spaced 24 bytes apart, but we don't know the spacing |
| // until relocations are processed, and in any case it is possible |
| // for an object to have some entries spaced 16 bytes apart and |
| // others 24 bytes apart.) |
| size_t |
| opd_ent_ndx(size_t off) const |
| { return off >> 4;} |
| |
| // Per object unique identifier |
| uint32_t uniq_; |
| |
| // For 32-bit the .got2 section shdnx, for 64-bit the .opd section shndx. |
| unsigned int special_; |
| |
| // For 64-bit the .rela.toc and .toc section shdnx. |
| unsigned int relatoc_; |
| unsigned int toc_; |
| |
| // For 64-bit, whether this object uses small model relocs to access |
| // the toc. |
| bool has_small_toc_reloc_; |
| |
| // Set at the start of gc_process_relocs, when we know opd_ent_ |
| // vector is valid. The flag could be made atomic and set in |
| // do_read_relocs with memory_order_release and then tested with |
| // memory_order_acquire, potentially resulting in fewer entries in |
| // access_from_map_. |
| bool opd_valid_; |
| |
| // Header e_flags |
| elfcpp::Elf_Word e_flags_; |
| |
| // For 64-bit, an array with one entry per 64-bit word in the .toc |
| // section, set if accesses using that word cannot be optimised. |
| std::vector<bool> no_toc_opt_; |
| |
| // The first 8-byte word of an OPD entry gives the address of the |
| // entry point of the function. Relocatable object files have a |
| // relocation on this word. The following vector records the |
| // section and offset specified by these relocations. |
| std::vector<Opd_ent> opd_ent_; |
| |
| // References made to this object's .opd section when running |
| // gc_process_relocs for another object, before the opd_ent_ vector |
| // is valid for this object. |
| Access_from access_from_map_; |
| |
| // Whether input section has a 14-bit branch reloc. |
| std::vector<bool> has14_; |
| |
| // The stub table to use for a given input section. |
| std::vector<unsigned int> stub_table_index_; |
| |
| // ELF st_other field for local symbols. |
| std::vector<unsigned char> st_other_; |
| |
| // Object attributes if there is a .gnu.attributes section. |
| Attributes_section_data* attributes_section_data_; |
| }; |
| |
| template<int size, bool big_endian> |
| class Powerpc_dynobj : public Sized_dynobj<size, big_endian> |
| { |
| public: |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; |
| |
| Powerpc_dynobj(const std::string& name, Input_file* input_file, off_t offset, |
| const typename elfcpp::Ehdr<size, big_endian>& ehdr) |
| : Sized_dynobj<size, big_endian>(name, input_file, offset, ehdr), |
| opd_shndx_(0), e_flags_(ehdr.get_e_flags()), opd_ent_(), |
| attributes_section_data_(NULL) |
| { |
| this->set_abiversion(0); |
| } |
| |
| ~Powerpc_dynobj() |
| { delete this->attributes_section_data_; } |
| |
| // Call Sized_dynobj::do_read_symbols to read the symbols then |
| // read .opd from a dynamic object, filling in opd_ent_ vector, |
| void |
| do_read_symbols(Read_symbols_data*); |
| |
| // The .opd section shndx. |
| unsigned int |
| opd_shndx() const |
| { |
| return this->opd_shndx_; |
| } |
| |
| // The .opd section address. |
| Address |
| opd_address() const |
| { |
| return this->opd_address_; |
| } |
| |
| // Init OPD entry arrays. |
| void |
| init_opd(size_t opd_size) |
| { |
| size_t count = this->opd_ent_ndx(opd_size); |
| this->opd_ent_.resize(count); |
| } |
| |
| // Return section and offset of function entry for .opd + R_OFF. |
| unsigned int |
| get_opd_ent(Address r_off, Address* value = NULL) const |
| { |
| size_t ndx = this->opd_ent_ndx(r_off); |
| gold_assert(ndx < this->opd_ent_.size()); |
| gold_assert(this->opd_ent_[ndx].shndx != 0); |
| if (value != NULL) |
| *value = this->opd_ent_[ndx].off; |
| return this->opd_ent_[ndx].shndx; |
| } |
| |
| // Set section and offset of function entry for .opd + R_OFF. |
| void |
| set_opd_ent(Address r_off, unsigned int shndx, Address value) |
| { |
| size_t ndx = this->opd_ent_ndx(r_off); |
| gold_assert(ndx < this->opd_ent_.size()); |
| this->opd_ent_[ndx].shndx = shndx; |
| 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); |
| |
| // The contents of the .gnu.attributes section if there is one. |
| const Attributes_section_data* |
| attributes_section_data() const |
| { return this->attributes_section_data_; } |
| |
| private: |
| // Used to specify extent of executable sections. |
| struct Sec_info |
| { |
| Sec_info(Address start_, Address len_, unsigned int shndx_) |
| : start(start_), len(len_), shndx(shndx_) |
| { } |
| |
| bool |
| operator<(const Sec_info& that) const |
| { return this->start < that.start; } |
| |
| Address start; |
| Address len; |
| unsigned int shndx; |
| }; |
| |
| struct Opd_ent |
| { |
| unsigned int shndx; |
| Address off; |
| }; |
| |
| // Return index into opd_ent_ array for .opd entry at OFF. |
| size_t |
| opd_ent_ndx(size_t off) const |
| { return off >> 4;} |
| |
| // For 64-bit the .opd section shndx and address. |
| unsigned int opd_shndx_; |
| Address opd_address_; |
| |
| // Header e_flags |
| elfcpp::Elf_Word e_flags_; |
| |
| // The first 8-byte word of an OPD entry gives the address of the |
| // entry point of the function. Records the section and offset |
| // corresponding to the address. Note that in dynamic objects, |
| // offset is *not* relative to the section. |
| std::vector<Opd_ent> opd_ent_; |
| |
| // Object attributes if there is a .gnu.attributes section. |
| Attributes_section_data* attributes_section_data_; |
| }; |
| |
| // Powerpc_copy_relocs class. Needed to peek at dynamic relocs the |
| // base class will emit. |
| |
| template<int sh_type, int size, bool big_endian> |
| class Powerpc_copy_relocs : public Copy_relocs<sh_type, size, big_endian> |
| { |
| public: |
| Powerpc_copy_relocs() |
| : Copy_relocs<sh_type, size, big_endian>(elfcpp::R_POWERPC_COPY) |
| { } |
| |
| // Emit any saved relocations which turn out to be needed. This is |
| // called after all the relocs have been scanned. |
| void |
| emit(Output_data_reloc<sh_type, true, size, big_endian>*); |
| }; |
| |
| // The types of GOT entries needed for this platform. |
| // These values are exposed to the ABI in an incremental link, but |
| // powerpc does not support incremental linking as yet. |
| enum Got_type |
| { |
| GOT_TYPE_STANDARD = 0, |
| GOT_TYPE_TLSGD = 1, // double entry for @got@tlsgd |
| GOT_TYPE_DTPREL = 2, // entry for @got@dtprel |
| GOT_TYPE_TPREL = 3, // entry for @got@tprel |
| GOT_TYPE_SMALL = 4, |
| GOT_TYPE_SMALL_TLSGD = 5, |
| GOT_TYPE_SMALL_DTPREL = 6, |
| GOT_TYPE_SMALL_TPREL = 7 |
| }; |
| |
| // gsym->needs_plt_entry purpose is to decide whether a non-branch |
| // reloc should reference a plt entry. It can't be used to decide |
| // whether branches need a plt entry. In fact the call to |
| // needs_plt_entry here is not needed; All cases where it might |
| // return true ought to be covered already. However, since this |
| // function is used to decide between plt_ and lplt_ sections in |
| // plt_off, make certain that every case where make_plt_entry puts |
| // entries in plt_ is covered here. |
| static bool |
| branch_needs_plt_entry(const Symbol* gsym) |
| { |
| return (((!gsym->is_defined() |
| || gsym->is_from_dynobj() |
| || gsym->is_preemptible()) |
| && !gsym->final_value_is_known()) |
| || gsym->needs_plt_entry()); |
| } |
| |
| template<int size, bool big_endian> |
| class Target_powerpc : public Sized_target<size, big_endian> |
| { |
| public: |
| typedef |
| Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian> Reloc_section; |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; |
| typedef typename elfcpp::Elf_types<size>::Elf_Swxword Signed_address; |
| typedef Unordered_set<Symbol_location, Symbol_location_hash> Tocsave_loc; |
| static const Address invalid_address = static_cast<Address>(0) - 1; |
| // Offset of tp and dtp pointers from start of TLS block. |
| static const Address tp_offset = 0x7000; |
| static const Address dtp_offset = 0x8000; |
| |
| Target_powerpc() |
| : Sized_target<size, big_endian>(&powerpc_info), |
| got_(NULL), biggot_(NULL), plt_(NULL), iplt_(NULL), lplt_(NULL), |
| brlt_section_(NULL), glink_(NULL), rela_dyn_(NULL), copy_relocs_(), |
| tlsld_got_offset_(-1U), |
| stub_tables_(), branch_lookup_table_(), branch_info_(), tocsave_loc_(), |
| power10_relocs_(false), plt_thread_safe_(false), plt_localentry0_(false), |
| plt_localentry0_init_(false), has_localentry0_(false), |
| has_tls_get_addr_opt_(false), no_tprel_opt_(false), |
| relax_failed_(false), relax_fail_count_(0), |
| stub_group_size_(0), savres_section_(0), |
| tls_get_addr_(NULL), tls_get_addr_opt_(NULL), |
| attributes_section_data_(NULL), |
| last_fp_(NULL), last_ld_(NULL), last_vec_(NULL), last_struct_(NULL) |
| { |
| } |
| |
| // Process the relocations to determine unreferenced sections for |
| // garbage collection. |
| void |
| gc_process_relocs(Symbol_table* symtab, |
| Layout* layout, |
| Sized_relobj_file<size, big_endian>* object, |
| unsigned int data_shndx, |
| 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); |
| |
| // Scan the relocations to look for symbol adjustments. |
| void |
| scan_relocs(Symbol_table* symtab, |
| Layout* layout, |
| Sized_relobj_file<size, big_endian>* object, |
| unsigned int data_shndx, |
| 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); |
| |
| // Map input .toc section to output .got section. |
| const char* |
| do_output_section_name(const Relobj*, const char* name, size_t* plen) const |
| { |
| if (size == 64 && strcmp(name, ".toc") == 0) |
| { |
| *plen = 4; |
| return ".got"; |
| } |
| return NULL; |
| } |
| |
| // Provide linker defined save/restore functions. |
| void |
| define_save_restore_funcs(Layout*, Symbol_table*); |
| |
| // No stubs unless a final link. |
| bool |
| do_may_relax() const |
| { return !parameters->options().relocatable(); } |
| |
| bool |
| do_relax(int, const Input_objects*, Symbol_table*, Layout*, const Task*); |
| |
| void |
| do_plt_fde_location(const Output_data*, unsigned char*, |
| uint64_t*, off_t*) const; |
| |
| // Stash info about branches, for stub generation. |
| void |
| push_branch(Powerpc_relobj<size, big_endian>* ppc_object, |
| unsigned int data_shndx, Address r_offset, |
| unsigned int r_type, unsigned int r_sym, Address addend) |
| { |
| Branch_info info(ppc_object, data_shndx, r_offset, r_type, r_sym, addend); |
| this->branch_info_.push_back(info); |
| if (r_type == elfcpp::R_POWERPC_REL14 |
| || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN |
| || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN) |
| ppc_object->set_has_14bit_branch(data_shndx); |
| } |
| |
| // Return whether the last branch is a plt call, and if so, mark the |
| // branch as having an R_PPC64_TOCSAVE. |
| bool |
| mark_pltcall(Powerpc_relobj<size, big_endian>* ppc_object, |
| unsigned int data_shndx, Address r_offset, Symbol_table* symtab) |
| { |
| return (size == 64 |
| && !this->branch_info_.empty() |
| && this->branch_info_.back().mark_pltcall(ppc_object, data_shndx, |
| r_offset, this, symtab)); |
| } |
| |
| // Say the given location, that of a nop in a function prologue with |
| // an R_PPC64_TOCSAVE reloc, will be used to save r2. |
| // R_PPC64_TOCSAVE relocs on nops following calls point at this nop. |
| void |
| add_tocsave(Powerpc_relobj<size, big_endian>* ppc_object, |
| unsigned int shndx, Address offset) |
| { |
| Symbol_location loc; |
| loc.object = ppc_object; |
| loc.shndx = shndx; |
| loc.offset = offset; |
| this->tocsave_loc_.insert(loc); |
| } |
| |
| // Accessor |
| const Tocsave_loc* |
| tocsave_loc() const |
| { |
| return &this->tocsave_loc_; |
| } |
| |
| void |
| do_define_standard_symbols(Symbol_table*, Layout*); |
| |
| // Finalize the sections. |
| void |
| do_finalize_sections(Layout*, const Input_objects*, Symbol_table*); |
| |
| // Get the custom dynamic tag value. |
| unsigned int |
| do_dynamic_tag_custom_value(elfcpp::DT) const; |
| |
| // Return the value to use for a dynamic which requires special |
| // treatment. |
| uint64_t |
| do_dynsym_value(const Symbol*) const; |
| |
| // Return the PLT address to use for a local symbol. |
| uint64_t |
| do_plt_address_for_local(const Relobj*, unsigned int) const; |
| |
| // Return the PLT address to use for a global symbol. |
| uint64_t |
| do_plt_address_for_global(const Symbol*) const; |
| |
| // Return the offset to use for the GOT_INDX'th got entry which is |
| // for a local tls symbol specified by OBJECT, SYMNDX. |
| int64_t |
| do_tls_offset_for_local(const Relobj* object, |
| unsigned int symndx, |
| Output_data_got_base* got, |
| unsigned int got_indx, |
| uint64_t addend) const; |
| |
| // Return the offset to use for the GOT_INDX'th got entry which is |
| // for global tls symbol GSYM. |
| int64_t |
| do_tls_offset_for_global(Symbol* gsym, |
| Output_data_got_base* got, unsigned int got_indx, |
| uint64_t addend) const; |
| |
| void |
| do_function_location(Symbol_location*) const; |
| |
| bool |
| do_can_check_for_function_pointers() const |
| { return true; } |
| |
| // Adjust -fsplit-stack code which calls non-split-stack code. |
| 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; |
| |
| // Relocate a section. |
| void |
| relocate_section(const Relocate_info<size, big_endian>*, |
| unsigned int sh_type, |
| const unsigned char* prelocs, |
| size_t reloc_count, |
| Output_section* output_section, |
| bool needs_special_offset_handling, |
| unsigned char* view, |
| Address view_address, |
| section_size_type view_size, |
| const Reloc_symbol_changes*); |
| |
| // Scan the relocs during a relocatable link. |
| void |
| scan_relocatable_relocs(Symbol_table* symtab, |
| Layout* layout, |
| Sized_relobj_file<size, big_endian>* object, |
| unsigned int data_shndx, |
| 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, |
| Relocatable_relocs*); |
| |
| // Scan the relocs for --emit-relocs. |
| void |
| emit_relocs_scan(Symbol_table* symtab, |
| Layout* layout, |
| Sized_relobj_file<size, big_endian>* object, |
| unsigned int data_shndx, |
| 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_syms, |
| Relocatable_relocs* rr); |
| |
| // Emit relocations for a section. |
| void |
| relocate_relocs(const Relocate_info<size, big_endian>*, |
| unsigned int sh_type, |
| const unsigned char* prelocs, |
| size_t reloc_count, |
| Output_section* output_section, |
| typename elfcpp::Elf_types<size>::Elf_Off |
| offset_in_output_section, |
| unsigned char*, |
| Address view_address, |
| section_size_type, |
| unsigned char* reloc_view, |
| section_size_type reloc_view_size); |
| |
| // Return whether SYM is defined by the ABI. |
| bool |
| do_is_defined_by_abi(const Symbol* sym) const |
| { |
| return strcmp(sym->name(), "__tls_get_addr") == 0; |
| } |
| |
| // Return the size of the GOT section, for incremental linking |
| section_size_type |
| got_size() const |
| { |
| gold_assert(this->got_ != NULL); |
| return this->got_->data_size() + (this->biggot_ |
| ? this->biggot_->data_size() : 0); |
| } |
| |
| // Get the PLT section. |
| const Output_data_plt_powerpc<size, big_endian>* |
| plt_section() const |
| { |
| gold_assert(this->plt_ != NULL); |
| return this->plt_; |
| } |
| |
| // Get the IPLT section. |
| const Output_data_plt_powerpc<size, big_endian>* |
| iplt_section() const |
| { |
| gold_assert(this->iplt_ != NULL); |
| return this->iplt_; |
| } |
| |
| // Get the LPLT section. |
| const Output_data_plt_powerpc<size, big_endian>* |
| lplt_section() const |
| { |
| return this->lplt_; |
| } |
| |
| // Return the plt offset and section for the given global sym. |
| Address |
| plt_off(const Symbol* gsym, |
| const Output_data_plt_powerpc<size, big_endian>** sec) const |
| { |
| if (gsym->type() == elfcpp::STT_GNU_IFUNC |
| && gsym->can_use_relative_reloc(false)) |
| *sec = this->iplt_section(); |
| else if (branch_needs_plt_entry(gsym)) |
| *sec = this->plt_section(); |
| else |
| *sec = this->lplt_section(); |
| return gsym->plt_offset(); |
| } |
| |
| // Return the plt offset and section for the given local sym. |
| Address |
| plt_off(const Sized_relobj_file<size, big_endian>* relobj, |
| unsigned int local_sym_index, |
| const Output_data_plt_powerpc<size, big_endian>** sec) const |
| { |
| const Symbol_value<size>* lsym = relobj->local_symbol(local_sym_index); |
| if (lsym->is_ifunc_symbol()) |
| *sec = this->iplt_section(); |
| else |
| *sec = this->lplt_section(); |
| return relobj->local_plt_offset(local_sym_index); |
| } |
| |
| // Get the .glink section. |
| const Output_data_glink<size, big_endian>* |
| glink_section() const |
| { |
| gold_assert(this->glink_ != NULL); |
| return this->glink_; |
| } |
| |
| Output_data_glink<size, big_endian>* |
| glink_section() |
| { |
| gold_assert(this->glink_ != NULL); |
| return this->glink_; |
| } |
| |
| bool has_glink() const |
| { return this->glink_ != NULL; } |
| |
| // Get the GOT section. |
| const Output_data_got_powerpc<size, big_endian>* |
| got_section(Got_type got_type) const |
| { |
| gold_assert(this->got_ != NULL); |
| if (size == 32 || (got_type & GOT_TYPE_SMALL)) |
| return this->got_; |
| gold_assert(this->biggot_ != NULL); |
| return this->biggot_; |
| } |
| |
| // Get the GOT section, creating it if necessary. |
| Output_data_got_powerpc<size, big_endian>* |
| got_section(Symbol_table*, Layout*, Got_type); |
| |
| // The toc/got pointer reg will be set to this value. |
| Address |
| toc_pointer() const |
| { |
| return this->got_->address() + this->got_->g_o_t(); |
| } |
| |
| // Offset of base used to access the GOT/TOC relative to the GOT section. |
| Address |
| got_base_offset(Got_type got_type) const |
| { |
| if (size == 32 || (got_type & GOT_TYPE_SMALL)) |
| return this->got_->g_o_t(); |
| return this->toc_pointer() - this->biggot_->address(); |
| } |
| |
| Object* |
| do_make_elf_object(const std::string&, Input_file*, off_t, |
| const elfcpp::Ehdr<size, big_endian>&); |
| |
| // Return the number of entries in the GOT. |
| unsigned int |
| got_entry_count() const |
| { |
| if (this->got_ == NULL) |
| return 0; |
| return this->got_size() / (size / 8); |
| } |
| |
| // Return the number of entries in the PLT. |
| unsigned int |
| plt_entry_count() const; |
| |
| // Return the offset of the first non-reserved PLT entry. |
| unsigned int |
| 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 |
| { |
| if (size == 32) |
| return 4; |
| if (this->abiversion() >= 2) |
| return 8; |
| return 24; |
| } |
| |
| Output_data_save_res<size, big_endian>* |
| savres_section() const |
| { |
| return this->savres_section_; |
| } |
| |
| // Add any special sections for this symbol to the gc work list. |
| // For powerpc64, this adds the code section of a function |
| // descriptor. |
| void |
| do_gc_mark_symbol(Symbol_table* symtab, Symbol* sym) const; |
| |
| // Handle target specific gc actions when adding a gc reference from |
| // SRC_OBJ, SRC_SHNDX to a location specified by DST_OBJ, DST_SHNDX |
| // and DST_OFF. For powerpc64, this adds a referenc to the code |
| // section of a function descriptor. |
| void |
| do_gc_add_reference(Symbol_table* symtab, |
| Relobj* src_obj, |
| unsigned int src_shndx, |
| Relobj* dst_obj, |
| unsigned int dst_shndx, |
| Address dst_off) const; |
| |
| typedef std::vector<Stub_table<size, big_endian>*> Stub_tables; |
| const Stub_tables& |
| stub_tables() const |
| { return this->stub_tables_; } |
| |
| const Output_data_brlt_powerpc<size, big_endian>* |
| brlt_section() const |
| { return this->brlt_section_; } |
| |
| void |
| add_branch_lookup_table(Address to) |
| { |
| unsigned int off = this->branch_lookup_table_.size() * (size / 8); |
| this->branch_lookup_table_.insert(std::make_pair(to, off)); |
| } |
| |
| Address |
| find_branch_lookup_table(Address to) |
| { |
| typename Branch_lookup_table::const_iterator p |
| = this->branch_lookup_table_.find(to); |
| return p == this->branch_lookup_table_.end() ? invalid_address : p->second; |
| } |
| |
| void |
| write_branch_lookup_table(unsigned char *oview) |
| { |
| for (typename Branch_lookup_table::const_iterator p |
| = this->branch_lookup_table_.begin(); |
| p != this->branch_lookup_table_.end(); |
| ++p) |
| { |
| elfcpp::Swap<size, big_endian>::writeval(oview + p->second, p->first); |
| } |
| } |
| |
| // Wrapper used after relax to define a local symbol in output data, |
| // from the end if value < 0. |
| void |
| define_local(Symbol_table* symtab, const char* name, |
| Output_data* od, Address value, unsigned int symsize) |
| { |
| Symbol* sym |
| = symtab->define_in_output_data(name, NULL, Symbol_table::PREDEFINED, |
| od, value, symsize, elfcpp::STT_NOTYPE, |
| elfcpp::STB_LOCAL, elfcpp::STV_HIDDEN, 0, |
| static_cast<Signed_address>(value) < 0, |
| false); |
| // We are creating this symbol late, so need to fix up things |
| // done early in Layout::finalize. |
| sym->set_dynsym_index(-1U); |
| } |
| |
| void |
| set_power10_relocs() |
| { |
| this->power10_relocs_ = true; |
| } |
| |
| bool |
| power10_stubs() const |
| { |
| return (this->power10_relocs_ |
| && (parameters->options().power10_stubs_enum() |
| != General_options::POWER10_STUBS_NO)); |
| } |
| |
| bool |
| power10_stubs_auto() const |
| { |
| return (parameters->options().power10_stubs_enum() |
| == General_options::POWER10_STUBS_AUTO); |
| } |
| |
| bool |
| plt_thread_safe() const |
| { return this->plt_thread_safe_; } |
| |
| bool |
| plt_localentry0() const |
| { return this->plt_localentry0_; } |
| |
| bool |
| has_localentry0() const |
| { return this->has_localentry0_; } |
| |
| void |
| set_has_localentry0() |
| { |
| this->has_localentry0_ = true; |
| } |
| |
| bool |
| is_elfv2_localentry0(const Symbol* gsym) const |
| { |
| return (size == 64 |
| && this->abiversion() >= 2 |
| && this->plt_localentry0() |
| && gsym->type() == elfcpp::STT_FUNC |
| && gsym->is_defined() |
| && gsym->nonvis() >> 3 == 0 |
| && !gsym->non_zero_localentry()); |
| } |
| |
| bool |
| is_elfv2_localentry0(const Sized_relobj_file<size, big_endian>* object, |
| unsigned int r_sym) const |
| { |
| const Powerpc_relobj<size, big_endian>* ppc_object |
| = static_cast<const Powerpc_relobj<size, big_endian>*>(object); |
| |
| if (size == 64 |
| && this->abiversion() >= 2 |
| && this->plt_localentry0() |
| && ppc_object->st_other(r_sym) >> 5 == 0) |
| { |
| const Symbol_value<size>* psymval = object->local_symbol(r_sym); |
| bool is_ordinary; |
| if (!psymval->is_ifunc_symbol() |
| && psymval->input_shndx(&is_ordinary) != elfcpp::SHN_UNDEF |
| && is_ordinary) |
| return true; |
| } |
| return false; |
| } |
| |
| bool |
| tprel_opt() const |
| { return !this->no_tprel_opt_ && parameters->options().tls_optimize(); } |
| |
| void |
| set_no_tprel_opt() |
| { this->no_tprel_opt_ = true; } |
| |
| // Remember any symbols seen with non-zero localentry, even those |
| // not providing a definition |
| bool |
| resolve(Symbol* to, const elfcpp::Sym<size, big_endian>& sym, Object*, |
| const char*) |
| { |
| if (size == 64) |
| { |
| unsigned char st_other = sym.get_st_other(); |
| if ((st_other & elfcpp::STO_PPC64_LOCAL_MASK) != 0) |
| to->set_non_zero_localentry(); |
| } |
| // We haven't resolved anything, continue normal processing. |
| return false; |
| } |
| |
| 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); |
| } |
| |
| Symbol* |
| tls_get_addr_opt() const |
| { return this->tls_get_addr_opt_; } |
| |
| Symbol* |
| tls_get_addr() const |
| { return this->tls_get_addr_; } |
| |
| // If optimizing __tls_get_addr calls, whether this is the |
| // "__tls_get_addr" symbol. |
| bool |
| is_tls_get_addr_opt(const Symbol* gsym) const |
| { |
| return this->tls_get_addr_opt_ && (gsym == this->tls_get_addr_ |
| || gsym == this->tls_get_addr_opt_); |
| } |
| |
| bool |
| replace_tls_get_addr(const Symbol* gsym) const |
| { return this->tls_get_addr_opt_ && gsym == this->tls_get_addr_; } |
| |
| void |
| set_has_tls_get_addr_opt() |
| { this->has_tls_get_addr_opt_ = true; } |
| |
| // Offset to toc save stack slot |
| int |
| stk_toc() const |
| { return this->abiversion() < 2 ? 40 : 24; } |
| |
| // Offset to linker save stack slot. ELFv2 doesn't have a linker word, |
| // so use the CR save slot. Used only by __tls_get_addr call stub, |
| // relying on __tls_get_addr not saving CR itself. |
| int |
| stk_linker() const |
| { return this->abiversion() < 2 ? 32 : 8; } |
| |
| // Merge object attributes from input object with those in the output. |
| void |
| merge_object_attributes(const Object*, const Attributes_section_data*); |
| |
| bool |
| symval_for_branch(const Symbol_table* symtab, |
| const Sized_symbol<size>* gsym, |
| Powerpc_relobj<size, big_endian>* object, |
| Address *value, unsigned int *dest_shndx); |
| |
| private: |
| |
| class Track_tls |
| { |
| public: |
| enum Tls_get_addr |
| { |
| NOT_EXPECTED = 0, |
| EXPECTED = 1, |
| SKIP = 2, |
| NORMAL = 3 |
| }; |
| |
| Track_tls() |
| : tls_get_addr_state_(NOT_EXPECTED), |
| relinfo_(NULL), relnum_(0), r_offset_(0) |
| { } |
| |
| ~Track_tls() |
| { |
| if (this->tls_get_addr_state_ != NOT_EXPECTED) |
| this->missing(); |
| } |
| |
| void |
| missing(void) |
| { |
| if (this->relinfo_ != NULL) |
| gold_error_at_location(this->relinfo_, this->relnum_, this->r_offset_, |
| _("missing expected __tls_get_addr call")); |
| } |
| |
| void |
| expect_tls_get_addr_call( |
| const Relocate_info<size, big_endian>* relinfo, |
| size_t relnum, |
| Address r_offset) |
| { |
| this->tls_get_addr_state_ = EXPECTED; |
| this->relinfo_ = relinfo; |
| this->relnum_ = relnum; |
| this->r_offset_ = r_offset; |
| } |
| |
| void |
| expect_tls_get_addr_call() |
| { this->tls_get_addr_state_ = EXPECTED; } |
| |
| void |
| skip_next_tls_get_addr_call() |
| {this->tls_get_addr_state_ = SKIP; } |
| |
| Tls_get_addr |
| maybe_skip_tls_get_addr_call(Target_powerpc<size, big_endian>* target, |
| unsigned int r_type, const Symbol* gsym) |
| { |
| bool is_tls_call |
| = ((r_type == elfcpp::R_POWERPC_REL24 |
| || (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC) |
| || r_type == elfcpp::R_PPC64_REL24_P9NOTOC |
| || r_type == elfcpp::R_PPC_PLTREL24 |
| || is_plt16_reloc<size>(r_type) |
| || r_type == elfcpp::R_PPC64_PLT_PCREL34 |
| || r_type == elfcpp::R_PPC64_PLT_PCREL34_NOTOC |
| || r_type == elfcpp::R_POWERPC_PLTSEQ |
| || r_type == elfcpp::R_POWERPC_PLTCALL |
| || r_type == elfcpp::R_PPC64_PLTSEQ_NOTOC |
| || r_type == elfcpp::R_PPC64_PLTCALL_NOTOC) |
| && gsym != NULL |
| && (gsym == target->tls_get_addr() |
| || gsym == target->tls_get_addr_opt())); |
| Tls_get_addr last_tls = this->tls_get_addr_state_; |
| this->tls_get_addr_state_ = NOT_EXPECTED; |
| if (is_tls_call && last_tls != EXPECTED) |
| return last_tls; |
| else if (!is_tls_call && last_tls != NOT_EXPECTED) |
| { |
| this->missing(); |
| return EXPECTED; |
| } |
| return NORMAL; |
| } |
| |
| private: |
| // What we're up to regarding calls to __tls_get_addr. |
| // On powerpc, the branch and link insn making a call to |
| // __tls_get_addr is marked with a relocation, R_PPC64_TLSGD, |
| // R_PPC64_TLSLD, R_PPC_TLSGD or R_PPC_TLSLD, in addition to the |
| // usual R_POWERPC_REL24 or R_PPC_PLTREL24 relocation on a call. |
| // The marker relocation always comes first, and has the same |
| // symbol as the reloc on the insn setting up the __tls_get_addr |
| // argument. This ties the arg setup insn with the call insn, |
| // allowing ld to safely optimize away the call. We check that |
| // every call to __tls_get_addr has a marker relocation, and that |
| // every marker relocation is on a call to __tls_get_addr. |
| Tls_get_addr tls_get_addr_state_; |
| // Info about the last reloc for error message. |
| const Relocate_info<size, big_endian>* relinfo_; |
| size_t relnum_; |
| Address r_offset_; |
| }; |
| |
| // The class which scans relocations. |
| class Scan : protected Track_tls |
| { |
| public: |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; |
| |
| Scan() |
| : Track_tls(), issued_non_pic_error_(false) |
| { } |
| |
| static inline int |
| get_reference_flags(unsigned int r_type, const Target_powerpc* target); |
| |
| inline void |
| local(Symbol_table* symtab, Layout* layout, Target_powerpc* target, |
| Sized_relobj_file<size, big_endian>* object, |
| unsigned int data_shndx, |
| Output_section* output_section, |
| const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type, |
| const elfcpp::Sym<size, big_endian>& lsym, |
| bool is_discarded); |
| |
| inline void |
| global(Symbol_table* symtab, Layout* layout, Target_powerpc* target, |
| Sized_relobj_file<size, big_endian>* object, |
| unsigned int data_shndx, |
| Output_section* output_section, |
| const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type, |
| Symbol* gsym); |
| |
| inline bool |
| local_reloc_may_be_function_pointer(Symbol_table* , Layout* , |
| Target_powerpc* , |
| Sized_relobj_file<size, big_endian>* relobj, |
| unsigned int , |
| Output_section* , |
| const elfcpp::Rela<size, big_endian>& , |
| unsigned int r_type, |
| const elfcpp::Sym<size, big_endian>&) |
| { |
| // PowerPC64 .opd is not folded, so any identical function text |
| // may be folded and we'll still keep function addresses distinct. |
| // That means no reloc is of concern here. |
| if (size == 64) |
| { |
| Powerpc_relobj<size, big_endian>* ppcobj = static_cast |
| <Powerpc_relobj<size, big_endian>*>(relobj); |
| if (ppcobj->abiversion() == 1) |
| return false; |
| } |
| // For 32-bit and ELFv2, conservatively assume anything but calls to |
| // function code might be taking the address of the function. |
| return !is_branch_reloc<size>(r_type); |
| } |
| |
| inline bool |
| global_reloc_may_be_function_pointer(Symbol_table* , Layout* , |
| Target_powerpc* , |
| Sized_relobj_file<size, big_endian>* relobj, |
| unsigned int , |
| Output_section* , |
| const elfcpp::Rela<size, big_endian>& , |
| unsigned int r_type, |
| Symbol*) |
| { |
| // As above. |
| if (size == 64) |
| { |
| Powerpc_relobj<size, big_endian>* ppcobj = static_cast |
| <Powerpc_relobj<size, big_endian>*>(relobj); |
| if (ppcobj->abiversion() == 1) |
| return false; |
| } |
| return !is_branch_reloc<size>(r_type); |
| } |
| |
| static bool |
| reloc_needs_plt_for_ifunc(Target_powerpc<size, big_endian>* target, |
| Sized_relobj_file<size, big_endian>* object, |
| unsigned int r_type, bool report_err); |
| |
| private: |
| static void |
| unsupported_reloc_local(Sized_relobj_file<size, big_endian>*, |
| unsigned int r_type); |
| |
| static void |
| unsupported_reloc_global(Sized_relobj_file<size, big_endian>*, |
| unsigned int r_type, Symbol*); |
| |
| static void |
| generate_tls_call(Symbol_table* symtab, Layout* layout, |
| Target_powerpc* target); |
| |
| void |
| check_non_pic(Relobj*, unsigned int r_type); |
| |
| // Whether we have issued an error about a non-PIC compilation. |
| bool issued_non_pic_error_; |
| }; |
| |
| // The class which implements relocation. |
| class Relocate : protected Track_tls |
| { |
| public: |
| // Use 'at' branch hints when true, 'y' when false. |
| // FIXME maybe: set this with an option. |
| static const bool is_isa_v2 = true; |
| |
| Relocate() |
| : Track_tls() |
| { } |
| |
| // Do a relocation. Return false if the caller should not issue |
| // any warnings about this relocation. |
| inline bool |
| relocate(const Relocate_info<size, big_endian>*, unsigned int, |
| Target_powerpc*, Output_section*, size_t, const unsigned char*, |
| const Sized_symbol<size>*, const Symbol_value<size>*, |
| unsigned char*, typename elfcpp::Elf_types<size>::Elf_Addr, |
| section_size_type); |
| }; |
| |
| class Relocate_comdat_behavior |
| { |
| public: |
| // Decide what the linker should do for relocations that refer to |
| // discarded comdat sections. |
| inline Comdat_behavior |
| get(const char* name) |
| { |
| gold::Default_comdat_behavior default_behavior; |
| Comdat_behavior ret = default_behavior.get(name); |
| if (ret == CB_ERROR) |
| { |
| if (size == 32 |
| && (strcmp(name, ".fixup") == 0 |
| || strcmp(name, ".got2") == 0)) |
| ret = CB_IGNORE; |
| if (size == 64 |
| && (strcmp(name, ".opd") == 0 |
| || strcmp(name, ".toc") == 0 |
| || strcmp(name, ".toc1") == 0)) |
| ret = CB_IGNORE; |
| } |
| return ret; |
| } |
| }; |
| |
| // Optimize the TLS relocation type based on what we know about the |
| // symbol. IS_FINAL is true if the final address of this symbol is |
| // known at link time. |
| |
| tls::Tls_optimization |
| optimize_tls_gd(bool is_final) |
| { |
| // If we are generating a shared library, then we can't do anything |
| // in the linker. |
| if (parameters->options().shared() |
| || !parameters->options().tls_optimize()) |
| return tls::TLSOPT_NONE; |
| |
| if (!is_final) |
| return tls::TLSOPT_TO_IE; |
| return tls::TLSOPT_TO_LE; |
| } |
| |
| tls::Tls_optimization |
| optimize_tls_ld() |
| { |
| if (parameters->options().shared() |
| || !parameters->options().tls_optimize()) |
| return tls::TLSOPT_NONE; |
| |
| return tls::TLSOPT_TO_LE; |
| } |
| |
| tls::Tls_optimization |
| optimize_tls_ie(bool is_final) |
| { |
| if (!is_final |
| || parameters->options().shared() |
| || !parameters->options().tls_optimize()) |
| return tls::TLSOPT_NONE; |
| |
| return tls::TLSOPT_TO_LE; |
| } |
| |
| // Create glink. |
| void |
| make_glink_section(Layout*); |
| |
| // Create the PLT section. |
| void |
| make_plt_section(Symbol_table*, Layout*); |
| |
| void |
| make_iplt_section(Symbol_table*, Layout*); |
| |
| void |
| make_lplt_section(Symbol_table*, Layout*); |
| |
| void |
| make_brlt_section(Layout*); |
| |
| // Create a PLT entry for a global symbol. |
| void |
| make_plt_entry(Symbol_table*, Layout*, Symbol*); |
| |
| // Create a PLT entry for a local IFUNC symbol. |
| void |
| make_local_ifunc_plt_entry(Symbol_table*, Layout*, |
| Sized_relobj_file<size, big_endian>*, |
| unsigned int); |
| |
| // Create a PLT entry for a local non-IFUNC symbol. |
| void |
| make_local_plt_entry(Symbol_table*, Layout*, |
| Sized_relobj_file<size, big_endian>*, |
| unsigned int); |
| |
| void |
| make_local_plt_entry(Symbol_table*, Layout*, Symbol*); |
| |
| // Create a GOT entry for local dynamic __tls_get_addr. |
| unsigned int |
| tlsld_got_offset(Symbol_table* symtab, Layout* layout, |
| Sized_relobj_file<size, big_endian>* object); |
| |
| unsigned int |
| tlsld_got_offset() const |
| { |
| return this->tlsld_got_offset_; |
| } |
| |
| // Get the dynamic reloc section, creating it if necessary. |
| Reloc_section* |
| rela_dyn_section(Layout*); |
| |
| // Similarly, but for ifunc symbols get the one for ifunc. |
| Reloc_section* |
| rela_dyn_section(Symbol_table*, Layout*, bool for_ifunc); |
| |
| // Copy a relocation against a global symbol. |
| void |
| copy_reloc(Symbol_table* symtab, Layout* layout, |
| Sized_relobj_file<size, big_endian>* object, |
| unsigned int shndx, Output_section* output_section, |
| Symbol* sym, const elfcpp::Rela<size, big_endian>& reloc) |
| { |
| unsigned int r_type = elfcpp::elf_r_type<size>(reloc.get_r_info()); |
| this->copy_relocs_.copy_reloc(symtab, layout, |
| symtab->get_sized_symbol<size>(sym), |
| object, shndx, output_section, |
| r_type, reloc.get_r_offset(), |
| reloc.get_r_addend(), |
| this->rela_dyn_section(layout)); |
| } |
| |
| // Look over all the input sections, deciding where to place stubs. |
| void |
| group_sections(Layout*, const Task*, bool); |
| |
| // Sort output sections by address. |
| struct Sort_sections |
| { |
| bool |
| operator()(const Output_section* sec1, const Output_section* sec2) |
| { return sec1->address() < sec2->address(); } |
| }; |
| |
| class Branch_info |
| { |
| public: |
| Branch_info(Powerpc_relobj<size, big_endian>* ppc_object, |
| unsigned int data_shndx, |
| Address r_offset, |
| unsigned int r_type, |
| unsigned int r_sym, |
| Address addend) |
| : object_(ppc_object), shndx_(data_shndx), offset_(r_offset), |
| r_type_(r_type), tocsave_ (0), r_sym_(r_sym), addend_(addend) |
| { } |
| |
| ~Branch_info() |
| { } |
| |
| // Return whether this branch is going via a plt call stub, and if |
| // so, mark it as having an R_PPC64_TOCSAVE. |
| bool |
| mark_pltcall(Powerpc_relobj<size, big_endian>* ppc_object, |
| unsigned int shndx, Address offset, |
| Target_powerpc* target, Symbol_table* symtab); |
| |
| // If this branch needs a plt call stub, or a long branch stub, make one. |
| bool |
| make_stub(Stub_table<size, big_endian>*, |
| Stub_table<size, big_endian>*, |
| Symbol_table*) const; |
| |
| private: |
| // The branch location.. |
| Powerpc_relobj<size, big_endian>* object_; |
| unsigned int shndx_; |
| Address offset_; |
| // ..and the branch type and destination. |
| unsigned int r_type_ : 31; |
| unsigned int tocsave_ : 1; |
| unsigned int r_sym_; |
| Address addend_; |
| }; |
| |
| // Information about this specific target which we pass to the |
| // general Target structure. |
| static Target::Target_info powerpc_info; |
| |
| // The small GOT section used by ppc32, and by ppc64 for entries that |
| // must be addresseed +/-32k from the got pointer. |
| Output_data_got_powerpc<size, big_endian>* got_; |
| // Another GOT section used for entries that can be addressed +/- 2G |
| // from the got pointer. |
| Output_data_got_powerpc<size, big_endian>* biggot_; |
| |
| // The PLT section. This is a container for a table of addresses, |
| // and their relocations. Each address in the PLT has a dynamic |
| // relocation (R_*_JMP_SLOT) and each address will have a |
| // corresponding entry in .glink for lazy resolution of the PLT. |
| // ppc32 initialises the PLT to point at the .glink entry, while |
| // ppc64 leaves this to ld.so. To make a call via the PLT, the |
| // linker adds a stub that loads the PLT entry into ctr then |
| // branches to ctr. There may be more than one stub for each PLT |
| // entry. DT_JMPREL points at the first PLT dynamic relocation and |
| // DT_PLTRELSZ gives the total size of PLT dynamic relocations. |
| Output_data_plt_powerpc<size, big_endian>* plt_; |
| // The IPLT section. Like plt_, this is a container for a table of |
| // addresses and their relocations, specifically for STT_GNU_IFUNC |
| // functions that resolve locally (STT_GNU_IFUNC functions that |
| // don't resolve locally go in PLT). Unlike plt_, these have no |
| // entry in .glink for lazy resolution, and the relocation section |
| // does not have a 1-1 correspondence with IPLT addresses. In fact, |
| // the relocation section may contain relocations against |
| // STT_GNU_IFUNC symbols at locations outside of IPLT. The |
| // relocation section will appear at the end of other dynamic |
| // relocations, so that ld.so applies these relocations after other |
| // dynamic relocations. In a static executable, the relocation |
| // section is emitted and marked with __rela_iplt_start and |
| // __rela_iplt_end symbols. |
| Output_data_plt_powerpc<size, big_endian>* iplt_; |
| // A PLT style section for local, non-ifunc symbols |
| Output_data_plt_powerpc<size, big_endian>* lplt_; |
| // Section holding long branch destinations. |
| Output_data_brlt_powerpc<size, big_endian>* brlt_section_; |
| // The .glink section. |
| Output_data_glink<size, big_endian>* glink_; |
| // The dynamic reloc section. |
| Reloc_section* rela_dyn_; |
| // Relocs saved to avoid a COPY reloc. |
| Powerpc_copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocs_; |
| // Offset of the GOT entry for local dynamic __tls_get_addr calls. |
| unsigned int tlsld_got_offset_; |
| |
| Stub_tables stub_tables_; |
| typedef Unordered_map<Address, unsigned int> Branch_lookup_table; |
| Branch_lookup_table branch_lookup_table_; |
| |
| typedef std::vector<Branch_info> Branches; |
| Branches branch_info_; |
| Tocsave_loc tocsave_loc_; |
| |
| off_t rela_dyn_size_; |
| |
| bool power10_relocs_; |
| bool plt_thread_safe_; |
| bool plt_localentry0_; |
| bool plt_localentry0_init_; |
| bool has_localentry0_; |
| bool has_tls_get_addr_opt_; |
| bool no_tprel_opt_; |
| |
| bool relax_failed_; |
| int relax_fail_count_; |
| int32_t stub_group_size_; |
| |
| Output_data_save_res<size, big_endian> *savres_section_; |
| |
| // The "__tls_get_addr" symbol, if present |
| Symbol* tls_get_addr_; |
| // If optimizing __tls_get_addr calls, the "__tls_get_addr_opt" symbol. |
| Symbol* tls_get_addr_opt_; |
| |
| // Attributes in output. |
| Attributes_section_data* attributes_section_data_; |
| |
| // Last input file to change various attribute tags |
| const char* last_fp_; |
| const char* last_ld_; |
| const char* last_vec_; |
| const char* last_struct_; |
| }; |
| |
| template<> |
| Target::Target_info Target_powerpc<32, true>::powerpc_info = |
| { |
| 32, // size |
| true, // is_big_endian |
| elfcpp::EM_PPC, // machine_code |
| false, // has_make_symbol |
| false, // has_resolve |
| false, // has_code_fill |
| true, // is_default_stack_executable |
| false, // can_icf_inline_merge_sections |
| '\0', // wrap_char |
| "/usr/lib/ld.so.1", // dynamic_linker |
| 0x10000000, // default_text_segment_address |
| 64 * 1024, // abi_pagesize (overridable by -z max-page-size) |
| 4 * 1024, // common_pagesize (overridable by -z common-page-size) |
| false, // isolate_execinstr |
| 0, // rosegment_gap |
| elfcpp::SHN_UNDEF, // small_common_shndx |
| elfcpp::SHN_UNDEF, // large_common_shndx |
| 0, // small_common_section_flags |
| 0, // large_common_section_flags |
| NULL, // attributes_section |
| NULL, // attributes_vendor |
| "_start", // entry_symbol_name |
| 32, // hash_entry_size |
| elfcpp::SHT_PROGBITS, // unwind_section_type |
| }; |
| |
| template<> |
| Target::Target_info Target_powerpc<32, false>::powerpc_info = |
| { |
| 32, // size |
| false, // is_big_endian |
| elfcpp::EM_PPC, // machine_code |
| false, // has_make_symbol |
| false, // has_resolve |
| false, // has_code_fill |
| true, // is_default_stack_executable |
| false, // can_icf_inline_merge_sections |
| '\0', // wrap_char |
| "/usr/lib/ld.so.1", // dynamic_linker |
| 0x10000000, // default_text_segment_address |
| 64 * 1024, // abi_pagesize (overridable by -z max-page-size) |
| 4 * 1024, // common_pagesize (overridable by -z common-page-size) |
| false, // isolate_execinstr |
| 0, // rosegment_gap |
| elfcpp::SHN_UNDEF, // small_common_shndx |
| elfcpp::SHN_UNDEF, // large_common_shndx |
| 0, // small_common_section_flags |
| 0, // large_common_section_flags |
| NULL, // attributes_section |
| NULL, // attributes_vendor |
| "_start", // entry_symbol_name |
| 32, // hash_entry_size |
| elfcpp::SHT_PROGBITS, // unwind_section_type |
| }; |
| |
| template<> |
| Target::Target_info Target_powerpc<64, true>::powerpc_info = |
| { |
| 64, // size |
| true, // is_big_endian |
| elfcpp::EM_PPC64, // machine_code |
| false, // has_make_symbol |
| true, // has_resolve |
| false, // has_code_fill |
| false, // is_default_stack_executable |
| false, // can_icf_inline_merge_sections |
| '\0', // wrap_char |
| "/usr/lib/ld.so.1", // dynamic_linker |
| 0x10000000, // default_text_segment_address |
| 64 * 1024, // abi_pagesize (overridable by -z max-page-size) |
| 4 * 1024, // common_pagesize (overridable by -z common-page-size) |
| false, // isolate_execinstr |
| 0, // rosegment_gap |
| elfcpp::SHN_UNDEF, // small_common_shndx |
| elfcpp::SHN_UNDEF, // large_common_shndx |
| 0, // small_common_section_flags |
| 0, // large_common_section_flags |
| NULL, // attributes_section |
| NULL, // attributes_vendor |
| "_start", // entry_symbol_name |
| 32, // hash_entry_size |
| elfcpp::SHT_PROGBITS, // unwind_section_type |
| }; |
| |
| template<> |
| Target::Target_info Target_powerpc<64, false>::powerpc_info = |
| { |
| 64, // size |
| false, // is_big_endian |
| elfcpp::EM_PPC64, // machine_code |
| false, // has_make_symbol |
| true, // has_resolve |
| false, // has_code_fill |
| false, // is_default_stack_executable |
| false, // can_icf_inline_merge_sections |
| '\0', // wrap_char |
| "/usr/lib/ld.so.1", // dynamic_linker |
| 0x10000000, // default_text_segment_address |
| 64 * 1024, // abi_pagesize (overridable by -z max-page-size) |
| 4 * 1024, // common_pagesize (overridable by -z common-page-size) |
| false, // isolate_execinstr |
| 0, // rosegment_gap |
| elfcpp::SHN_UNDEF, // small_common_shndx |
| elfcpp::SHN_UNDEF, // large_common_shndx |
| 0, // small_common_section_flags |
| 0, // large_common_section_flags |
| NULL, // attributes_section |
| NULL, // attributes_vendor |
| "_start", // entry_symbol_name |
| 32, // hash_entry_size |
| elfcpp::SHT_PROGBITS, // unwind_section_type |
| }; |
| |
| template<int size> |
| inline bool |
| is_branch_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_POWERPC_REL24 |
| || (size == 64 && r_type == elfcpp::R_PPC64_REL24_NOTOC) |
| || r_type == elfcpp::R_PPC64_REL24_P9NOTOC |
| || r_type == elfcpp::R_PPC_PLTREL24 |
| || r_type == elfcpp::R_PPC_LOCAL24PC |
| || r_type == elfcpp::R_POWERPC_REL14 |
| || r_type == elfcpp::R_POWERPC_REL14_BRTAKEN |
| || r_type == elfcpp::R_POWERPC_REL14_BRNTAKEN |
| || r_type == elfcpp::R_POWERPC_ADDR24 |
| || r_type == elfcpp::R_POWERPC_ADDR14 |
| || r_type == elfcpp::R_POWERPC_ADDR14_BRTAKEN |
| || r_type == elfcpp::R_POWERPC_ADDR14_BRNTAKEN); |
| } |
| |
| // Reloc resolves to plt entry. |
| template<int size> |
| inline bool |
| is_plt16_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_POWERPC_PLT16_LO |
| || r_type == elfcpp::R_POWERPC_PLT16_HI |
| || r_type == elfcpp::R_POWERPC_PLT16_HA |
| || (size == 64 && r_type == elfcpp::R_PPC64_PLT16_LO_DS)); |
| } |
| |
| // GOT_TYPE_STANDARD or GOT_TYPE_SMALL (ie. not TLS) GOT relocs |
| inline bool |
| is_got_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_POWERPC_GOT16 |
| || r_type == elfcpp::R_POWERPC_GOT16_LO |
| || r_type == elfcpp::R_POWERPC_GOT16_HI |
| || r_type == elfcpp::R_POWERPC_GOT16_HA |
| || r_type == elfcpp::R_PPC64_GOT16_DS |
| || r_type == elfcpp::R_PPC64_GOT16_LO_DS |
| || r_type == elfcpp::R_PPC64_GOT_PCREL34); |
| } |
| |
| // If INSN is an opcode that may be used with an @tls operand, return |
| // the transformed insn for TLS optimisation, otherwise return 0. If |
| // REG is non-zero only match an insn with RB or RA equal to REG. |
| uint32_t |
| at_tls_transform(uint32_t insn, unsigned int reg) |
| { |
| if ((insn & (0x3f << 26)) != 31 << 26) |
| return 0; |
| |
| unsigned int rtra; |
| if (reg == 0 || ((insn >> 11) & 0x1f) == reg) |
| rtra = insn & ((1 << 26) - (1 << 16)); |
| else if (((insn >> 16) & 0x1f) == reg) |
| rtra = (insn & (0x1f << 21)) | ((insn & (0x1f << 11)) << 5); |
| else |
| return 0; |
| |
| if ((insn & (0x3ff << 1)) == 266 << 1) |
| // add -> addi |
| insn = 14 << 26; |
| else if ((insn & (0x1f << 1)) == 23 << 1 |
| && ((insn & (0x1f << 6)) < 14 << 6 |
| || ((insn & (0x1f << 6)) >= 16 << 6 |
| && (insn & (0x1f << 6)) < 24 << 6))) |
| // load and store indexed -> dform |
| insn = (32 | ((insn >> 6) & 0x1f)) << 26; |
| else if ((insn & (((0x1a << 5) | 0x1f) << 1)) == 21 << 1) |
| // ldx, ldux, stdx, stdux -> ld, ldu, std, stdu |
| insn = ((58 | ((insn >> 6) & 4)) << 26) | ((insn >> 6) & 1); |
| else if ((insn & (((0x1f << 5) | 0x1f) << 1)) == 341 << 1) |
| // lwax -> lwa |
| insn = (58 << 26) | 2; |
| else |
| return 0; |
| insn |= rtra; |
| return insn; |
| } |
| |
| |
| template<int size, bool big_endian> |
| class Powerpc_relocate_functions |
| { |
| public: |
| enum Overflow_check |
| { |
| CHECK_NONE, |
| CHECK_SIGNED, |
| CHECK_UNSIGNED, |
| CHECK_BITFIELD, |
| CHECK_LOW_INSN, |
| CHECK_HIGH_INSN |
| }; |
| |
| enum Status |
| { |
| STATUS_OK, |
| STATUS_OVERFLOW |
| }; |
| |
| private: |
| typedef Powerpc_relocate_functions<size, big_endian> This; |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Address; |
| typedef typename elfcpp::Elf_types<size>::Elf_Swxword SignedAddress; |
| |
| template<int valsize> |
| static inline bool |
| has_overflow_signed(Address value) |
| { |
| // limit = 1 << (valsize - 1) without shift count exceeding size of type |
| Address limit = static_cast<Address>(1) << ((valsize - 1) >> 1); |
| limit <<= ((valsize - 1) >> 1); |
| limit <<= ((valsize - 1) - 2 * ((valsize - 1) >> 1)); |
| return value + limit > (limit << 1) - 1; |
| } |
| |
| template<int valsize> |
| static inline bool |
| has_overflow_unsigned(Address value) |
| { |
| Address limit = static_cast<Address>(1) << ((valsize - 1) >> 1); |
| limit <<= ((valsize - 1) >> 1); |
| limit <<= ((valsize - 1) - 2 * ((valsize - 1) >> 1)); |
| return value > (limit << 1) - 1; |
| } |
| |
| template<int valsize> |
| static inline bool |
| has_overflow_bitfield(Address value) |
| { |
| return (has_overflow_unsigned<valsize>(value) |
| && has_overflow_signed<valsize>(value)); |
| } |
| |
| template<int valsize> |
| static inline Status |
| overflowed(Address value, Overflow_check overflow) |
| { |
| if (overflow == CHECK_SIGNED) |
| { |
| if (has_overflow_signed<valsize>(value)) |
| return STATUS_OVERFLOW; |
| } |
| else if (overflow == CHECK_UNSIGNED) |
| { |
| if (has_overflow_unsigned<valsize>(value)) |
| return STATUS_OVERFLOW; |
| } |
| else if (overflow == CHECK_BITFIELD) |
| { |
| if (has_overflow_bitfield<valsize>(value)) |
| return STATUS_OVERFLOW; |
| } |
| return STATUS_OK; |
| } |
| |
| // Do a simple RELA relocation |
| template<int fieldsize, int valsize> |
| static inline Status |
| rela(unsigned char* view, Address value, Overflow_check overflow) |
| { |
| typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype; |
| Valtype* wv = reinterpret_cast<Valtype*>(view); |
| elfcpp::Swap<fieldsize, big_endian>::writeval(wv, value); |
| return overflowed<valsize>(value, overflow); |
| } |
| |
| template<int fieldsize, int valsize> |
| static inline Status |
| rela(unsigned char* view, |
| unsigned int right_shift, |
| typename elfcpp::Valtype_base<fieldsize>::Valtype dst_mask, |
| Address value, |
| Overflow_check overflow) |
| { |
| typedef typename elfcpp::Swap<fieldsize, big_endian>::Valtype Valtype; |
| Valtype* wv = reinterpret_cast<Valtype*>(view); |
| Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(wv); |
| if (overflow == CHECK_SIGNED) |
| value = static_cast<SignedAddress>(value) >> right_shift; |
| else |
| value = value >> right_shift; |
| Valtype reloc = value; |
| val &= ~dst_mask; |
| reloc &= dst_mask; |
| elfcpp::Swap<fieldsize, big_endian>::writeval(wv, val | reloc); |
| return overflowed<valsize>(value, overflow); |
| } |
| |
| // Do a simple RELA relocation, unaligned. |
| template<int fieldsize, int valsize> |
| static inline Status |
| rela_ua(unsigned char* view, Address value, Overflow_check overflow) |
| { |
| elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, value); |
| return overflowed<valsize>(value, overflow); |
| } |
| |
| template<int fieldsize, int valsize> |
| static inline Status |
| rela_ua(unsigned char* view, |
| unsigned int right_shift, |
| typename elfcpp::Valtype_base<fieldsize>::Valtype dst_mask, |
| Address value, |
| Overflow_check overflow) |
| { |
| typedef typename elfcpp::Swap_unaligned<fieldsize, big_endian>::Valtype |
| Valtype; |
| Valtype val = elfcpp::Swap<fieldsize, big_endian>::readval(view); |
| if (overflow == CHECK_SIGNED) |
| value = static_cast<SignedAddress>(value) >> right_shift; |
| else |
| value = value >> right_shift; |
| Valtype reloc = value; |
| val &= ~dst_mask; |
| reloc &= dst_mask; |
| elfcpp::Swap_unaligned<fieldsize, big_endian>::writeval(view, val | reloc); |
| return overflowed<valsize>(value, overflow); |
| } |
| |
| public: |
| // R_PPC64_ADDR64: (Symbol + Addend) |
| static inline void |
| addr64(unsigned char* view, Address value) |
| { This::template rela<64,64>(view, value, CHECK_NONE); } |
| |
| // R_PPC64_UADDR64: (Symbol + Addend) unaligned |
| static inline void |
| addr64_u(unsigned char* view, Address value) |
| { This::template rela_ua<64,64>(view, value, CHECK_NONE); } |
| |
| // R_POWERPC_ADDR32: (Symbol + Addend) |
| static inline Status |
| addr32(unsigned char* view, Address value, Overflow_check overflow) |
| { return This::template rela<32,32>(view, value, overflow); } |
| |
| // R_POWERPC_UADDR32: (Symbol + Addend) unaligned |
| static inline Status |
| addr32_u(unsigned char* view, Address value, Overflow_check overflow) |
| { return This::template rela_ua<32,32>(view, value, overflow); } |
| |
| // R_POWERPC_ADDR24: (Symbol + Addend) & 0x3fffffc |
| static inline Status |
| addr24(unsigned char* view, Address value, Overflow_check overflow) |
| { |
| Status stat = This::template rela<32,26>(view, 0, 0x03fffffc, |
| value, overflow); |
| if (overflow != CHECK_NONE && (value & 3) != 0) |
| stat = STATUS_OVERFLOW; |
| return stat; |
| } |
| |
| // R_POWERPC_ADDR16: (Symbol + Addend) & 0xffff |
| static inline Status |
| addr16(unsigned char* view, Address value, Overflow_check overflow) |
| { return This::template rela<16,16>(view, value, overflow); } |
| |
| // R_POWERPC_ADDR16: (Symbol + Addend) & 0xffff, unaligned |
| static inline Status |
| addr16_u(unsigned char* view, Address value, Overflow_check overflow) |
| { return This::template rela_ua<16,16>(view, value, overflow); } |
| |
| // R_POWERPC_ADDR16_DS: (Symbol + Addend) & 0xfffc |
| static inline Status |
| addr16_ds(unsigned char* view, Address value, Overflow_check overflow) |
| { |
| Status stat = This::template rela<16,16>(view, 0, 0xfffc, value, overflow); |
| if ((value & 3) != 0) |
| stat = STATUS_OVERFLOW; |
| return stat; |
| } |
| |
| // R_POWERPC_ADDR16_DQ: (Symbol + Addend) & 0xfff0 |
| static inline Status |
| addr16_dq(unsigned char* view, Address value, Overflow_check overflow) |
| { |
| Status stat = This::template rela<16,16>(view, 0, 0xfff0, value, overflow); |
| if ((value & 15) != 0) |
| stat = STATUS_OVERFLOW; |
| return stat; |
| } |
| |
| // R_POWERPC_ADDR16_HI: ((Symbol + Addend) >> 16) & 0xffff |
| static inline void |
| addr16_hi(unsigned char* view, Address value) |
| { This::template rela<16,16>(view, 16, 0xffff, value, CHECK_NONE); } |
| |
| // R_POWERPC_ADDR16_HA: ((Symbol + Addend + 0x8000) >> 16) & 0xffff |
| static inline void |
| addr16_ha(unsigned char* view, Address value) |
| { This::addr16_hi(view, value + 0x8000); } |
| |
| // R_POWERPC_ADDR16_HIGHER: ((Symbol + Addend) >> 32) & 0xffff |
| static inline void |
| addr16_hi2(unsigned char* view, Address value) |
| { This::template rela<16,16>(view, 32, 0xffff, value, CHECK_NONE); } |
| |
| // R_POWERPC_ADDR16_HIGHERA: ((Symbol + Addend + 0x8000) >> 32) & 0xffff |
| static inline void |
| addr16_ha2(unsigned char* view, Address value) |
| { This::addr16_hi2(view, value + 0x8000); } |
| |
| // R_POWERPC_ADDR16_HIGHEST: ((Symbol + Addend) >> 48) & 0xffff |
| static inline void |
| addr16_hi3(unsigned char* view, Address value) |
| { This::template rela<16,16>(view, 48, 0xffff, value, CHECK_NONE); } |
| |
| // R_POWERPC_ADDR16_HIGHESTA: ((Symbol + Addend + 0x8000) >> 48) & 0xffff |
| static inline void |
| addr16_ha3(unsigned char* view, Address value) |
| { This::addr16_hi3(view, value + 0x8000); } |
| |
| // R_POWERPC_ADDR14: (Symbol + Addend) & 0xfffc |
| static inline Status |
| addr14(unsigned char* view, Address value, Overflow_check overflow) |
| { |
| Status stat = This::template rela<32,16>(view, 0, 0xfffc, value, overflow); |
| if (overflow != CHECK_NONE && (value & 3) != 0) |
| stat = STATUS_OVERFLOW; |
| return stat; |
| } |
| |
| // R_POWERPC_REL16DX_HA |
| static inline Status |
| addr16dx_ha(unsigned char *view, Address value, Overflow_check overflow) |
| { |
| typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype; |
| Valtype* wv = reinterpret_cast<Valtype*>(view); |
| Valtype val = elfcpp::Swap<32, big_endian>::readval(wv); |
| value += 0x8000; |
| value = static_cast<SignedAddress>(value) >> 16; |
| val |= (value & 0xffc1) | ((value & 0x3e) << 15); |
| elfcpp::Swap<32, big_endian>::writeval(wv, val); |
| return overflowed<16>(value, overflow); |
| } |
| |
| // R_PPC64_D34 |
| static inline Status |
| addr34(unsigned char *view, uint64_t value, Overflow_check overflow) |
| { |
| Status stat = This::template rela<32,18>(view, 16, 0x3ffff, |
| value, overflow); |
| This::rela<32,16>(view + 4, 0, 0xffff, value, CHECK_NONE); |
| return stat; |
| } |
| |
| // R_PPC64_D34_HI30 |
| static inline void |
| addr34_hi(unsigned char *view, uint64_t value) |
| { This::addr34(view, value >> 34, CHECK_NONE);} |
| |
| // R_PPC64_D34_HA30 |
| static inline void |
| addr34_ha(unsigned char *view, uint64_t value) |
| { This::addr34_hi(view, value + (1ULL << 33));} |
| |
| // R_PPC64_D28 |
| static inline Status |
| addr28(unsigned char *view, uint64_t value, Overflow_check overflow) |
| { |
| Status stat = This::template rela<32,12>(view, 16, 0xfff, |
| value, overflow); |
| This::rela<32,16>(view + 4, 0, 0xffff, value, CHECK_NONE); |
| return stat; |
| } |
| |
| // R_PPC64_ADDR16_HIGHER34 |
| static inline void |
| addr16_higher34(unsigned char* view, uint64_t value) |
| { This::addr16(view, value >> 34, CHECK_NONE); } |
| |
| // R_PPC64_ADDR16_HIGHERA34 |
| static inline void |
| addr16_highera34(unsigned char* view, uint64_t value) |
| { This::addr16_higher34(view, value + (1ULL << 33)); } |
| |
| // R_PPC64_ADDR16_HIGHEST34 |
| static inline void |
| addr16_highest34(unsigned char* view, uint64_t value) |
| { This::addr16(view, value >> 50, CHECK_NONE); } |
| |
| // R_PPC64_ADDR16_HIGHESTA34 |
| static inline void |
| addr16_highesta34(unsigned char* view, uint64_t value) |
| { This::addr16_highest34(view, value + (1ULL << 33)); } |
| }; |
| |
| // Set ABI version for input and output. |
| |
| template<int size, bool big_endian> |
| void |
| Powerpc_relobj<size, big_endian>::set_abiversion(int ver) |
| { |
| this->e_flags_ |= ver; |
| if (this->abiversion() != 0) |
| { |
| Target_powerpc<size, big_endian>* target = |
| static_cast<Target_powerpc<size, big_endian>*>( |
| parameters->sized_target<size, big_endian>()); |
| 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, .opd, .rela.toc, and .toc in a |
| // relocatable object, if such sections exists. |
| |
| template<int size, bool big_endian> |
| bool |
| Powerpc_relobj<size, big_endian>::do_find_special_sections( |
| Read_symbols_data* sd) |
| { |
| const unsigned char* const pshdrs = sd->section_headers->data(); |
| const unsigned char* namesu = sd->section_names->data(); |
| const char* names = reinterpret_cast<const char*>(namesu); |
| section_size_type names_size = sd->section_names_size; |
| const unsigned char* s; |
| |
| s = this->template find_shdr<size, big_endian>(pshdrs, |
| size == 32 ? ".got2" : ".opd", |
| names, names_size, NULL); |
| if (s != NULL) |
| { |
| unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::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()); |
| } |
| } |
| if (size == 64) |
| { |
| s = this->template find_shdr<size, big_endian>(pshdrs, ".rela.toc", |
| names, names_size, NULL); |
| if (s != NULL) |
| { |
| unsigned int ndx = (s - pshdrs) / elfcpp::Elf_sizes<size>::shdr_size; |
| this->relatoc_ = ndx; |
| typename elfcpp::Shdr<size, big_endian> shdr(s); |
| this->toc_ = this->adjust_shndx(shdr.get_sh_info()); |
| } |
| } |
| return Sized_relobj_file<size, big_endian>::do_find_special_sections(sd); |
| } |
| |
| // Examine .rela.opd to build info about function entry points. |
| |
| template<int size, bool big_endian> |
| void |
| Powerpc_relobj<size, big_endian>::scan_opd_relocs( |
| size_t reloc_count, |
| const unsigned char* prelocs, |
| const unsigned char* plocal_syms) |
| { |
| if (size == 64) |
| { |
| typedef typename elfcpp::Rela<size, big_endian> Reltype; |
| const int reloc_size = elfcpp::Elf_sizes<size>::rela_size; |
| const int sym_size = elfcpp::Elf_sizes<size>::sym_size; |
| Address expected_off = 0; |
| bool regular = true; |
| unsigned int opd_ent_size = 0; |
| |
| for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) |
| { |
| Reltype reloc(prelocs); |
| typename elfcpp::Elf_types<size>::Elf_WXword r_info |
| = reloc.get_r_info(); |
| unsigned int r_type = elfcpp::elf_r_type<size>(r_info); |
| if (r_type == elfcpp::R_PPC64_ADDR64) |
| { |
| unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info); |
| typename elfcpp::Elf_types<size>::Elf_Addr value; |
| bool is_ordinary; |
| unsigned int shndx; |
| if (r_sym < this->local_symbol_count()) |
| { |
| typename elfcpp::Sym<size, big_endian> |
| lsym(plocal_syms + r_sym * sym_size); |
| shndx = lsym.get_st_shndx(); |
| shndx = this->adjust_sym_shndx(r_sym, shndx, &is_ordinary); |
| value = lsym.get_st_value(); |
| } |
| else |
| shndx = this->symbol_section_and_value(r_sym, &value, |
| &is_ordinary); |
| this->set_opd_ent(reloc.get_r_offset(), shndx, |
| value + reloc.get_r_addend()); |
| if (i == 2) |
| { |
| expected_off = reloc.get_r_offset(); |
| opd_ent_size = expected_off; |
| } |
| else if (expected_off != reloc.get_r_offset()) |
| regular = false; |
| expected_off += opd_ent_size; |
| } |
| else if (r_type == elfcpp::R_PPC64_TOC) |
| { |
| if (expected_off - opd_ent_size + 8 != reloc.get_r_offset()) |
| regular = false; |
| } |
| else |
| { |
| gold_warning(_("%s: unexpected reloc type %u in .opd section"), |
| this->name().c_str(), r_type); |
| regular = false; |
| } |
| } |
| if (reloc_count <= 2) |
| opd_ent_size = this->section_size(this->opd_shndx()); |
| if (opd_ent_size != 24 && opd_ent_size != 16) |
| regular = false; |
| if (!regular) |
| { |
| gold_warning(_("%s: .opd is not a regular array of opd entries"), |
| this->name().c_str()); |
| opd_ent_size = 0; |
| } |
| } |
| } |
| |
| // Returns true if a code sequence loading the TOC entry at VALUE |
| // relative to the TOC pointer can be converted into code calculating |
| // a TOC pointer relative offset. |
| // If so, the TOC pointer relative offset is stored to VALUE. |
| |
| template<int size, bool big_endian> |
| bool |
| Powerpc_relobj<size, big_endian>::make_toc_relative( |
| Target_powerpc<size, big_endian>* target, |
| Address* value) |
| { |
| if (size != 64) |
| return false; |
| |
| // With -mcmodel=medium code it is quite possible to have |
| // toc-relative relocs referring to objects outside the TOC. |
| // Don't try to look at a non-existent TOC. |
| if (this->toc_shndx() == 0 |
| || this->output_section(this->toc_shndx()) == 0) |
| return false; |
| |
| // Convert VALUE back to an address by adding got_base (see below), |
| // then to an offset in the TOC by subtracting the TOC output |
| // section address and the TOC output offset. |
| Address off = (*value + target->toc_pointer() |
| - this->output_section(this->toc_shndx())->address() |
| - this->output_section_offset(this->toc_shndx())); |
| // Is this offset in the TOC? -mcmodel=medium code may be using |
| // TOC relative access to variables outside the TOC. Those of |
| // course can't be optimized. We also don't try to optimize code |
| // that is using a different object's TOC. |
| if (off >= this->section_size(this->toc_shndx())) |
| return false; |
| |
| if (this->no_toc_opt(off)) |
| return false; |
| |
| section_size_type vlen; |
| unsigned char* view = this->get_output_view(this->toc_shndx(), &vlen); |
| Address addr = elfcpp::Swap<size, big_endian>::readval(view + off); |
| // The TOC pointer |
| Address got_base = target->toc_pointer(); |
| addr -= got_base; |
| if (addr + (uint64_t) 0x80008000 >= (uint64_t) 1 << 32) |
| return false; |
| |
| *value = addr; |
| return true; |
| } |
| |
| template<int size, bool big_endian> |
| bool |
| Powerpc_relobj<size, big_endian>::make_got_relative( |
| Target_powerpc<size, big_endian>* target, |
| const Symbol_value<size>* psymval, |
| Address addend, |
| Address* value) |
| { |
| Address addr = psymval->value(this, addend); |
| Address got_base = target->toc_pointer(); |
| addr -= got_base; |
| if (addr + 0x80008000 > 0xffffffff) |
| return false; |
| |
| *value = addr; |
| return true; |
| } |
| |
| // Perform the Sized_relobj_file method, then set up opd info from |
| // .opd relocs. |
| |
| template<int size, bool big_endian> |
| void |
| Powerpc_relobj<size, big_endian>::do_read_relocs(Read_relocs_data* rd) |
| { |
| Sized_relobj_file<size, big_endian>::do_read_relocs(rd); |
| if (size == 64) |
| { |
| for (Read_relocs_data::Relocs_list::iterator p = rd->relocs.begin(); |
| p != rd->relocs.end(); |
| ++p) |
| { |
| if (p->data_shndx == this->opd_shndx()) |
| { |
| uint64_t opd_size = this->section_size(this->opd_shndx()); |
| gold_assert(opd_size == static_cast<size_t>(opd_size)); |
| if (opd_size != 0) |
| { |
| this->init_opd(opd_size); |
| this->scan_opd_relocs(p->reloc_count, p->contents->data(), |
| rd->local_symbols->data()); |
| } |
| break; |
| } |
| } |
| } |
| } |
| |
| // Read the symbols then set up st_other vector. |
| |
| template<int size, bool big_endian> |
| void |
| Powerpc_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd) |
| { |
| this->base_read_symbols(sd); |
| if (this->input_file()->format() != Input_file::FORMAT_ELF) |
| return; |
| if (size == 64) |
| { |
| const int shdr_size = elfcpp::Elf_sizes<size>::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<size>::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<size, big_endian> 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<size, big_endian> 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); |
| } |
| } |
| } |
| } |
| |
| const size_t shdr_size = elfcpp::Elf_sizes<size>::shdr_size; |
| const unsigned char* ps = sd->section_headers->data() + shdr_size; |
| bool merge_attributes = false; |
| for (unsigned int i = 1; i < this->shnum(); ++i, ps += shdr_size) |
| { |
| elfcpp::Shdr<size, big_endian> shdr(ps); |
| switch (shdr.get_sh_type()) |
| { |
| case elfcpp::SHT_GNU_ATTRIBUTES: |
| { |
| gold_assert(this->attributes_section_data_ == NULL); |
| section_offset_type section_offset = shdr.get_sh_offset(); |
| section_size_type section_size = |
| convert_to_section_size_type(shdr.get_sh_size()); |
| const unsigned char* view = |
| this->get_view(section_offset, section_size, true, false); |
| this->attributes_section_data_ = |
| new Attributes_section_data(view, section_size); |
| } |
| break; |
| |
| case elfcpp::SHT_SYMTAB: |
| { |
| // Sometimes an object has no contents except the section |
| // name string table and an empty symbol table with the |
| // undefined symbol. We don't want to merge |
| // processor-specific flags from such an object. |
| const typename elfcpp::Elf_types<size>::Elf_WXword sym_size = |
| elfcpp::Elf_sizes<size>::sym_size; |
| if (shdr.get_sh_size() > sym_size) |
| merge_attributes = true; |
| } |
| break; |
| |
| case elfcpp::SHT_STRTAB: |
| break; |
| |
| default: |
| merge_attributes = true; |
| break; |
| } |
| } |
| |
| if (!merge_attributes) |
| { |
| // Should rarely happen. |
| delete this->attributes_section_data_; |
| this->attributes_section_data_ = NULL; |
| } |
| } |
| |
| template<int size, bool big_endian> |
| void |
| Powerpc_dynobj<size, big_endian>::set_abiversion(int ver) |
| { |
| this->e_flags_ |= ver; |
| if (this->abiversion() != 0) |
| { |
| Target_powerpc<size, big_endian>* target = |
| static_cast<Target_powerpc<size, big_endian>*>( |
| parameters->sized_target<size, big_endian>()); |
| 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::base_read_symbols to read the symbols then |
| // read .opd from a dynamic object, filling in opd_ent_ vector, |
| |
| template<int size, bool big_endian> |
| void |
| Powerpc_dynobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd) |
| { |
| this->base_read_symbols(sd); |
| const size_t shdr_size = elfcpp::Elf_sizes<size>::shdr_size; |
| const unsigned char* ps = |
| sd->section_headers->data() + shdr_size * (this->shnum() - 1); |
| for (unsigned int i = this->shnum(); i > 0; --i, ps -= shdr_size) |
| { |
| elfcpp::Shdr<size, big_endian> shdr(ps); |
| if (shdr.get_sh_type() == elfcpp::SHT_GNU_ATTRIBUTES) |
| { |
| section_offset_type section_offset = shdr.get_sh_offset(); |
| section_size_type section_size = |
| convert_to_section_size_type(shdr.get_sh_size()); |
| const unsigned char* view = |
| this->get_view(section_offset, section_size, true, false); |
| this->attributes_section_data_ = |
| new Attributes_section_data(view, section_size); |
| break; |
| } |
| } |
| if (size == 64) |
| { |
| const unsigned char* const pshdrs = sd->section_headers->data(); |
| const unsigned char* namesu = sd->section_names->data(); |
| const char* names = reinterpret_cast<const char*>(namesu); |
| const unsigned char* s = NULL; |
| const unsigned char* opd; |
| section_size_type opd_size; |
| |
| // Find and read .opd section. |
| while (1) |
| { |
| s = this->template find_shdr<size, big_endian>(pshdrs, ".opd", names, |
| sd->section_names_size, |
| s); |
| if (s == NULL) |
| return; |
| |
| typename elfcpp::Shdr<size, big_endian> shdr(s); |
| 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()); |
| opd = this->get_view(shdr.get_sh_offset(), opd_size, |
| true, false); |
| break; |
| } |
| } |
| |
| // Build set of executable sections. |
| // Using a set is probably overkill. There is likely to be only |
| // a few executable sections, typically .init, .text and .fini, |
| // and they are generally grouped together. |
| typedef std::set<Sec_info> Exec_sections; |
| Exec_sections exec_sections; |
| s = pshdrs; |
| for (unsigned int i = 1; i < this->shnum(); ++i, s += shdr_size) |
| { |
| typename elfcpp::Shdr<size, big_endian> shdr(s); |
| if (shdr.get_sh_type() == elfcpp::SHT_PROGBITS |
| && ((shdr.get_sh_flags() |
| & (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR)) |
| == (elfcpp::SHF_ALLOC | elfcpp::SHF_EXECINSTR)) |
| && shdr.get_sh_size() != 0) |
| { |
| exec_sections.insert(Sec_info(shdr.get_sh_addr(), |
| shdr.get_sh_size(), i)); |
| } |
| } |
| if (exec_sections.empty()) |
| return; |
| |
| // Look over the OPD entries. This is complicated by the fact |
| // that some binaries will use two-word entries while others |
| // will use the standard three-word entries. In most cases |
| // the third word (the environment pointer for languages like |
| // Pascal) is unused and will be zero. If the third word is |
| // used it should not be pointing into executable sections, |
| // I think. |
| this->init_opd(opd_size); |
| for (const unsigned char* p = opd; p < opd + opd_size; p += 8) |
| { |
| typedef typename elfcpp::Swap<64, big_endian>::Valtype Valtype; |
| const Valtype* valp = reinterpret_cast<const Valtype*>(p); |
| Valtype val = elfcpp::Swap<64, big_endian>::readval(valp); |
| if (val == 0) |
| // Chances are that this is the third word of an OPD entry. |
| continue; |
| typename Exec_sections::const_iterator e |
| = exec_sections.upper_bound(Sec_info(val, 0, 0)); |
| if (e != exec_sections.begin()) |
| { |
| --e; |
| if (e->start <= val && val < e->start + e->len) |
| { |
| // We have an address in an executable section. |
| // VAL ought to be the function entry, set it up. |
| this->set_opd_ent(p - opd, e->shndx, val); |
| // Skip second word of OPD entry, the TOC pointer. |
| p += 8; |
| } |
| } |
| // If we didn't match any executable sections, we likely |
| // have a non-zero third word in the OPD entry. |
| } |
| } |
| } |
| |
| // Relocate sections. |
| |
| template<int size, bool big_endian> |
| void |
| Powerpc_relobj<size, big_endian>::do_relocate_sections( |
| const Symbol_table* symtab, const Layout* layout, |
| const unsigned char* pshdrs, Output_file* of, |
| typename Sized_relobj_file<size, big_endian>::Views* pviews) |
| { |
| unsigned int start = 1; |
| if (size == 64 |
| && this->relatoc_ != 0 |
| && !parameters->options().relocatable()) |
| { |
| // Relocate .toc first. |
| this->relocate_section_range(symtab, layout, pshdrs, of, pviews, |
| this->relatoc_, this->relatoc_); |
| this->relocate_section_range(symtab, layout, pshdrs, of, pviews, |
| 1, this->relatoc_ - 1); |
| start = this->relatoc_ + 1; |
| } |
| this->relocate_section_range(symtab, layout, pshdrs, of, pviews, |
| start, this->shnum() - 1); |
| } |
| |
| // Set up some symbols. |
| |
| template<int size, bool big_endian> |
| void |
| Target_powerpc<size, big_endian>::do_define_standard_symbols( |
| Symbol_table* symtab, |
| Layout* layout) |
| { |
| if (size == 32) |
| { |
| // Define _GLOBAL_OFFSET_TABLE_ to ensure it isn't seen as |
| // undefined when scanning relocs (and thus requires |
| // non-relative dynamic relocs). The proper value will be |
| // updated later. |
| Symbol *gotsym = symtab->lookup("_GLOBAL_OFFSET_TABLE_", NULL); |
| if (gotsym != NULL && gotsym->is_undefined()) |
| { |
| Target_powerpc<size, big_endian>* target = |
| static_cast<Target_powerpc<size, big_endian>*>( |
| parameters->sized_target<size, big_endian>()); |
| Output_data_got_powerpc<size, big_endian>* got |
| = target->got_section(symtab, layout, GOT_TYPE_SMALL); |
| symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL, |
| Symbol_table::PREDEFINED, |
| got, 0, 0, |
| elfcpp::STT_OBJECT, |
| elfcpp::STB_LOCAL, |
| elfcpp::STV_HIDDEN, 0, |
| false, false); |
| } |
| |
| // Define _SDA_BASE_ at the start of the .sdata section + 32768. |
| Symbol *sdasym = symtab->lookup("_SDA_BASE_", NULL); |
| if (sdasym != NULL && sdasym->is_undefined()) |
| { |
| Output_data_space* sdata = new Output_data_space(4, "** sdata"); |
| Output_section* os |
| = layout->add_output_section_data(".sdata", 0, |
| elfcpp::SHF_ALLOC |
| | elfcpp::SHF_WRITE, |
| sdata, ORDER_SMALL_DATA, false); |
| symtab->define_in_output_data("_SDA_BASE_", NULL, |
| Symbol_table::PREDEFINED, |
| os, 32768, 0, elfcpp::STT_OBJECT, |
| elfcpp::STB_LOCAL, elfcpp::STV_HIDDEN, |
| 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<size, big_endian>* target = |
| static_cast<Target_powerpc<size, big_endian>*>( |
| parameters->sized_target<size, big_endian>()); |
| Output_data_got_powerpc<size, big_endian>* got |
| = target->got_section(symtab, layout, GOT_TYPE_SMALL); |
| 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); |
| } |
| } |
| |
| this->tls_get_addr_ = symtab->lookup("__tls_get_addr"); |
| if (parameters->options().tls_get_addr_optimize() |
| && this->tls_get_addr_ != NULL |
| && this->tls_get_addr_->in_reg()) |
| this->tls_get_addr_opt_ = symtab->lookup("__tls_get_addr_opt"); |
| if (this->tls_get_addr_opt_ != NULL) |
| { |
| if (this->tls_get_addr_->is_undefined() |
| || this->tls_get_addr_->is_from_dynobj()) |
| { |
| // Make it seem as if references to __tls_get_addr are |
| // really to __tls_get_addr_opt, so the latter symbol is |
| // made dynamic, not the former. |
| this->tls_get_addr_->clear_in_reg(); |
| this->tls_get_addr_opt_->set_in_reg(); |
| } |
| // We have a non-dynamic definition for __tls_get_addr. |
| // Make __tls_get_addr_opt the same, if it does not already have |
| // a non-dynamic definition. |
| else if (this->tls_get_addr_opt_->is_undefined() |
| || this->tls_get_addr_opt_->is_from_dynobj()) |
| { |
| Sized_symbol<size>* from |
| = static_cast<Sized_symbol<size>*>(this->tls_get_addr_); |
| Sized_symbol<size>* to |
| = static_cast<Sized_symbol<size>*>(this->tls_get_addr_opt_); |
| symtab->clone<size>(to, from); |
| } |
| } |
| } |
| |
| // Set up PowerPC target specific relobj. |
| |
| template<int size, bool big_endian> |
| Object* |
| Target_powerpc<size, big_endian>::do_make_elf_object( |
| const std::string& name, |
| Input_file* input_file, |
| off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr) |
| { |
| int et = ehdr.get_e_type(); |
| // ET_EXEC files are valid input for --just-symbols/-R, |
| // and we treat them as relocatable objects. |
| if (et == elfcpp::ET_REL |
| || (et == elfcpp::ET_EXEC && input_file->just_symbols())) |
| { |
| Powerpc_relobj<size, big_endian>* obj = |
|