| // mips.cc -- mips target support for gold. |
| |
| // Copyright (C) 2011-2021 Free Software Foundation, Inc. |
| // Written by Sasa Stankovic <sasa.stankovic@imgtec.com> |
| // and Aleksandar Simeonov <aleksandar.simeonov@rt-rk.com>. |
| // This file contains borrowed and adapted code from bfd/elfxx-mips.c. |
| |
| // 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 <algorithm> |
| #include <set> |
| #include <sstream> |
| #include "demangle.h" |
| |
| #include "elfcpp.h" |
| #include "parameters.h" |
| #include "reloc.h" |
| #include "mips.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" |
| #include "nacl.h" |
| |
| namespace |
| { |
| using namespace gold; |
| |
| template<int size, bool big_endian> |
| class Mips_output_data_plt; |
| |
| template<int size, bool big_endian> |
| class Mips_output_data_got; |
| |
| template<int size, bool big_endian> |
| class Target_mips; |
| |
| template<int size, bool big_endian> |
| class Mips_output_section_reginfo; |
| |
| template<int size, bool big_endian> |
| class Mips_output_section_options; |
| |
| template<int size, bool big_endian> |
| class Mips_output_data_la25_stub; |
| |
| template<int size, bool big_endian> |
| class Mips_output_data_mips_stubs; |
| |
| template<int size> |
| class Mips_symbol; |
| |
| template<int size, bool big_endian> |
| class Mips_got_info; |
| |
| template<int size, bool big_endian> |
| class Mips_relobj; |
| |
| class Mips16_stub_section_base; |
| |
| template<int size, bool big_endian> |
| class Mips16_stub_section; |
| |
| // The ABI says that every symbol used by dynamic relocations must have |
| // a global GOT entry. Among other things, this provides the dynamic |
| // linker with a free, directly-indexed cache. The GOT can therefore |
| // contain symbols that are not referenced by GOT relocations themselves |
| // (in other words, it may have symbols that are not referenced by things |
| // like R_MIPS_GOT16 and R_MIPS_GOT_PAGE). |
| |
| // GOT relocations are less likely to overflow if we put the associated |
| // GOT entries towards the beginning. We therefore divide the global |
| // GOT entries into two areas: "normal" and "reloc-only". Entries in |
| // the first area can be used for both dynamic relocations and GP-relative |
| // accesses, while those in the "reloc-only" area are for dynamic |
| // relocations only. |
| |
| // These GGA_* ("Global GOT Area") values are organised so that lower |
| // values are more general than higher values. Also, non-GGA_NONE |
| // values are ordered by the position of the area in the GOT. |
| |
| enum Global_got_area |
| { |
| GGA_NORMAL = 0, |
| GGA_RELOC_ONLY = 1, |
| GGA_NONE = 2 |
| }; |
| |
| // The types of GOT entries needed for this platform. |
| // These values are exposed to the ABI in an incremental link. |
| // Do not renumber existing values without changing the version |
| // number of the .gnu_incremental_inputs section. |
| enum Got_type |
| { |
| GOT_TYPE_STANDARD = 0, // GOT entry for a regular symbol |
| GOT_TYPE_TLS_OFFSET = 1, // GOT entry for TLS offset |
| GOT_TYPE_TLS_PAIR = 2, // GOT entry for TLS module/offset pair |
| |
| // GOT entries for multi-GOT. We support up to 1024 GOTs in multi-GOT links. |
| GOT_TYPE_STANDARD_MULTIGOT = 3, |
| GOT_TYPE_TLS_OFFSET_MULTIGOT = GOT_TYPE_STANDARD_MULTIGOT + 1024, |
| GOT_TYPE_TLS_PAIR_MULTIGOT = GOT_TYPE_TLS_OFFSET_MULTIGOT + 1024 |
| }; |
| |
| // TLS type of GOT entry. |
| enum Got_tls_type |
| { |
| GOT_TLS_NONE = 0, |
| GOT_TLS_GD = 1, |
| GOT_TLS_LDM = 2, |
| GOT_TLS_IE = 4 |
| }; |
| |
| // Values found in the r_ssym field of a relocation entry. |
| enum Special_relocation_symbol |
| { |
| RSS_UNDEF = 0, // None - value is zero. |
| RSS_GP = 1, // Value of GP. |
| RSS_GP0 = 2, // Value of GP in object being relocated. |
| RSS_LOC = 3 // Address of location being relocated. |
| }; |
| |
| // Whether the section is readonly. |
| static inline bool |
| is_readonly_section(Output_section* output_section) |
| { |
| elfcpp::Elf_Xword section_flags = output_section->flags(); |
| elfcpp::Elf_Word section_type = output_section->type(); |
| |
| if (section_type == elfcpp::SHT_NOBITS) |
| return false; |
| |
| if (section_flags & elfcpp::SHF_WRITE) |
| return false; |
| |
| return true; |
| } |
| |
| // Return TRUE if a relocation of type R_TYPE from OBJECT might |
| // require an la25 stub. See also local_pic_function, which determines |
| // whether the destination function ever requires a stub. |
| template<int size, bool big_endian> |
| static inline bool |
| relocation_needs_la25_stub(Mips_relobj<size, big_endian>* object, |
| unsigned int r_type, bool target_is_16_bit_code) |
| { |
| // We specifically ignore branches and jumps from EF_PIC objects, |
| // where the onus is on the compiler or programmer to perform any |
| // necessary initialization of $25. Sometimes such initialization |
| // is unnecessary; for example, -mno-shared functions do not use |
| // the incoming value of $25, and may therefore be called directly. |
| if (object->is_pic()) |
| return false; |
| |
| switch (r_type) |
| { |
| case elfcpp::R_MIPS_26: |
| case elfcpp::R_MIPS_PC16: |
| case elfcpp::R_MIPS_PC21_S2: |
| case elfcpp::R_MIPS_PC26_S2: |
| case elfcpp::R_MICROMIPS_26_S1: |
| case elfcpp::R_MICROMIPS_PC7_S1: |
| case elfcpp::R_MICROMIPS_PC10_S1: |
| case elfcpp::R_MICROMIPS_PC16_S1: |
| case elfcpp::R_MICROMIPS_PC23_S2: |
| return true; |
| |
| case elfcpp::R_MIPS16_26: |
| return !target_is_16_bit_code; |
| |
| default: |
| return false; |
| } |
| } |
| |
| // Return true if SYM is a locally-defined PIC function, in the sense |
| // that it or its fn_stub might need $25 to be valid on entry. |
| // Note that MIPS16 functions set up $gp using PC-relative instructions, |
| // so they themselves never need $25 to be valid. Only non-MIPS16 |
| // entry points are of interest here. |
| template<int size, bool big_endian> |
| static inline bool |
| local_pic_function(Mips_symbol<size>* sym) |
| { |
| bool def_regular = (sym->source() == Symbol::FROM_OBJECT |
| && !sym->object()->is_dynamic() |
| && !sym->is_undefined()); |
| |
| if (sym->is_defined() && def_regular) |
| { |
| Mips_relobj<size, big_endian>* object = |
| static_cast<Mips_relobj<size, big_endian>*>(sym->object()); |
| |
| if ((object->is_pic() || sym->is_pic()) |
| && (!sym->is_mips16() |
| || (sym->has_mips16_fn_stub() && sym->need_fn_stub()))) |
| return true; |
| } |
| return false; |
| } |
| |
| static inline bool |
| hi16_reloc(int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_HI16 |
| || r_type == elfcpp::R_MIPS16_HI16 |
| || r_type == elfcpp::R_MICROMIPS_HI16 |
| || r_type == elfcpp::R_MIPS_PCHI16); |
| } |
| |
| static inline bool |
| lo16_reloc(int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_LO16 |
| || r_type == elfcpp::R_MIPS16_LO16 |
| || r_type == elfcpp::R_MICROMIPS_LO16 |
| || r_type == elfcpp::R_MIPS_PCLO16); |
| } |
| |
| static inline bool |
| got16_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_GOT16 |
| || r_type == elfcpp::R_MIPS16_GOT16 |
| || r_type == elfcpp::R_MICROMIPS_GOT16); |
| } |
| |
| static inline bool |
| call_lo16_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_CALL_LO16 |
| || r_type == elfcpp::R_MICROMIPS_CALL_LO16); |
| } |
| |
| static inline bool |
| got_lo16_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_GOT_LO16 |
| || r_type == elfcpp::R_MICROMIPS_GOT_LO16); |
| } |
| |
| static inline bool |
| eh_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_EH); |
| } |
| |
| static inline bool |
| got_disp_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_GOT_DISP |
| || r_type == elfcpp::R_MICROMIPS_GOT_DISP); |
| } |
| |
| static inline bool |
| got_page_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_GOT_PAGE |
| || r_type == elfcpp::R_MICROMIPS_GOT_PAGE); |
| } |
| |
| static inline bool |
| tls_gd_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_TLS_GD |
| || r_type == elfcpp::R_MIPS16_TLS_GD |
| || r_type == elfcpp::R_MICROMIPS_TLS_GD); |
| } |
| |
| static inline bool |
| tls_gottprel_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_TLS_GOTTPREL |
| || r_type == elfcpp::R_MIPS16_TLS_GOTTPREL |
| || r_type == elfcpp::R_MICROMIPS_TLS_GOTTPREL); |
| } |
| |
| static inline bool |
| tls_ldm_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_TLS_LDM |
| || r_type == elfcpp::R_MIPS16_TLS_LDM |
| || r_type == elfcpp::R_MICROMIPS_TLS_LDM); |
| } |
| |
| static inline bool |
| mips16_call_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS16_26 |
| || r_type == elfcpp::R_MIPS16_CALL16); |
| } |
| |
| static inline bool |
| jal_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MIPS_26 |
| || r_type == elfcpp::R_MIPS16_26 |
| || r_type == elfcpp::R_MICROMIPS_26_S1); |
| } |
| |
| static inline bool |
| micromips_branch_reloc(unsigned int r_type) |
| { |
| return (r_type == elfcpp::R_MICROMIPS_26_S1 |
| || r_type == elfcpp::R_MICROMIPS_PC16_S1 |
| || r_type == elfcpp::R_MICROMIPS_PC10_S1 |
| || r_type == elfcpp::R_MICROMIPS_PC7_S1); |
| } |
| |
| // Check if R_TYPE is a MIPS16 reloc. |
| static inline bool |
| mips16_reloc(unsigned int r_type) |
| { |
| switch (r_type) |
| { |
| case elfcpp::R_MIPS16_26: |
| case elfcpp::R_MIPS16_GPREL: |
| case elfcpp::R_MIPS16_GOT16: |
| case elfcpp::R_MIPS16_CALL16: |
| case elfcpp::R_MIPS16_HI16: |
| case elfcpp::R_MIPS16_LO16: |
| case elfcpp::R_MIPS16_TLS_GD: |
| case elfcpp::R_MIPS16_TLS_LDM: |
| case elfcpp::R_MIPS16_TLS_DTPREL_HI16: |
| case elfcpp::R_MIPS16_TLS_DTPREL_LO16: |
| case elfcpp::R_MIPS16_TLS_GOTTPREL: |
| case elfcpp::R_MIPS16_TLS_TPREL_HI16: |
| case elfcpp::R_MIPS16_TLS_TPREL_LO16: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| // Check if R_TYPE is a microMIPS reloc. |
| static inline bool |
| micromips_reloc(unsigned int r_type) |
| { |
| switch (r_type) |
| { |
| case elfcpp::R_MICROMIPS_26_S1: |
| case elfcpp::R_MICROMIPS_HI16: |
| case elfcpp::R_MICROMIPS_LO16: |
| case elfcpp::R_MICROMIPS_GPREL16: |
| case elfcpp::R_MICROMIPS_LITERAL: |
| case elfcpp::R_MICROMIPS_GOT16: |
| case elfcpp::R_MICROMIPS_PC7_S1: |
| case elfcpp::R_MICROMIPS_PC10_S1: |
| case elfcpp::R_MICROMIPS_PC16_S1: |
| case elfcpp::R_MICROMIPS_CALL16: |
| case elfcpp::R_MICROMIPS_GOT_DISP: |
| case elfcpp::R_MICROMIPS_GOT_PAGE: |
| case elfcpp::R_MICROMIPS_GOT_OFST: |
| case elfcpp::R_MICROMIPS_GOT_HI16: |
| case elfcpp::R_MICROMIPS_GOT_LO16: |
| case elfcpp::R_MICROMIPS_SUB: |
| case elfcpp::R_MICROMIPS_HIGHER: |
| case elfcpp::R_MICROMIPS_HIGHEST: |
| case elfcpp::R_MICROMIPS_CALL_HI16: |
| case elfcpp::R_MICROMIPS_CALL_LO16: |
| case elfcpp::R_MICROMIPS_SCN_DISP: |
| case elfcpp::R_MICROMIPS_JALR: |
| case elfcpp::R_MICROMIPS_HI0_LO16: |
| case elfcpp::R_MICROMIPS_TLS_GD: |
| case elfcpp::R_MICROMIPS_TLS_LDM: |
| case elfcpp::R_MICROMIPS_TLS_DTPREL_HI16: |
| case elfcpp::R_MICROMIPS_TLS_DTPREL_LO16: |
| case elfcpp::R_MICROMIPS_TLS_GOTTPREL: |
| case elfcpp::R_MICROMIPS_TLS_TPREL_HI16: |
| case elfcpp::R_MICROMIPS_TLS_TPREL_LO16: |
| case elfcpp::R_MICROMIPS_GPREL7_S2: |
| case elfcpp::R_MICROMIPS_PC23_S2: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| static inline bool |
| is_matching_lo16_reloc(unsigned int high_reloc, unsigned int lo16_reloc) |
| { |
| switch (high_reloc) |
| { |
| case elfcpp::R_MIPS_HI16: |
| case elfcpp::R_MIPS_GOT16: |
| return lo16_reloc == elfcpp::R_MIPS_LO16; |
| case elfcpp::R_MIPS_PCHI16: |
| return lo16_reloc == elfcpp::R_MIPS_PCLO16; |
| case elfcpp::R_MIPS16_HI16: |
| case elfcpp::R_MIPS16_GOT16: |
| return lo16_reloc == elfcpp::R_MIPS16_LO16; |
| case elfcpp::R_MICROMIPS_HI16: |
| case elfcpp::R_MICROMIPS_GOT16: |
| return lo16_reloc == elfcpp::R_MICROMIPS_LO16; |
| default: |
| return false; |
| } |
| } |
| |
| // This class is used to hold information about one GOT entry. |
| // There are three types of entry: |
| // |
| // (1) a SYMBOL + OFFSET address, where SYMBOL is local to an input object |
| // (object != NULL, symndx >= 0, tls_type != GOT_TLS_LDM) |
| // (2) a SYMBOL address, where SYMBOL is not local to an input object |
| // (sym != NULL, symndx == -1) |
| // (3) a TLS LDM slot (there's only one of these per GOT.) |
| // (object != NULL, symndx == 0, tls_type == GOT_TLS_LDM) |
| |
| template<int size, bool big_endian> |
| class Mips_got_entry |
| { |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Mips_address; |
| |
| public: |
| Mips_got_entry(Mips_relobj<size, big_endian>* object, unsigned int symndx, |
| Mips_address addend, unsigned char tls_type, |
| unsigned int shndx, bool is_section_symbol) |
| : addend_(addend), symndx_(symndx), tls_type_(tls_type), |
| is_section_symbol_(is_section_symbol), shndx_(shndx) |
| { this->d.object = object; } |
| |
| Mips_got_entry(Mips_symbol<size>* sym, unsigned char tls_type) |
| : addend_(0), symndx_(-1U), tls_type_(tls_type), |
| is_section_symbol_(false), shndx_(-1U) |
| { this->d.sym = sym; } |
| |
| // Return whether this entry is for a local symbol. |
| bool |
| is_for_local_symbol() const |
| { return this->symndx_ != -1U; } |
| |
| // Return whether this entry is for a global symbol. |
| bool |
| is_for_global_symbol() const |
| { return this->symndx_ == -1U; } |
| |
| // Return the hash of this entry. |
| size_t |
| hash() const |
| { |
| if (this->tls_type_ == GOT_TLS_LDM) |
| return this->symndx_ + (1 << 18); |
| |
| size_t name_hash_value = gold::string_hash<char>( |
| (this->symndx_ != -1U) |
| ? this->d.object->name().c_str() |
| : this->d.sym->name()); |
| size_t addend = this->addend_; |
| return name_hash_value ^ this->symndx_ ^ (addend << 16); |
| } |
| |
| // Return whether this entry is equal to OTHER. |
| bool |
| equals(Mips_got_entry<size, big_endian>* other) const |
| { |
| if (this->symndx_ != other->symndx_ |
| || this->tls_type_ != other->tls_type_) |
| return false; |
| |
| if (this->tls_type_ == GOT_TLS_LDM) |
| return true; |
| |
| return (((this->symndx_ != -1U) |
| ? (this->d.object == other->d.object) |
| : (this->d.sym == other->d.sym)) |
| && (this->addend_ == other->addend_)); |
| } |
| |
| // Return input object that needs this GOT entry. |
| Mips_relobj<size, big_endian>* |
| object() const |
| { |
| gold_assert(this->symndx_ != -1U); |
| return this->d.object; |
| } |
| |
| // Return local symbol index for local GOT entries. |
| unsigned int |
| symndx() const |
| { |
| gold_assert(this->symndx_ != -1U); |
| return this->symndx_; |
| } |
| |
| // Return the relocation addend for local GOT entries. |
| Mips_address |
| addend() const |
| { return this->addend_; } |
| |
| // Return global symbol for global GOT entries. |
| Mips_symbol<size>* |
| sym() const |
| { |
| gold_assert(this->symndx_ == -1U); |
| return this->d.sym; |
| } |
| |
| // Return whether this is a TLS GOT entry. |
| bool |
| is_tls_entry() const |
| { return this->tls_type_ != GOT_TLS_NONE; } |
| |
| // Return TLS type of this GOT entry. |
| unsigned char |
| tls_type() const |
| { return this->tls_type_; } |
| |
| // Return section index of the local symbol for local GOT entries. |
| unsigned int |
| shndx() const |
| { return this->shndx_; } |
| |
| // Return whether this is a STT_SECTION symbol. |
| bool |
| is_section_symbol() const |
| { return this->is_section_symbol_; } |
| |
| private: |
| // The addend. |
| Mips_address addend_; |
| |
| // The index of the symbol if we have a local symbol; -1 otherwise. |
| unsigned int symndx_; |
| |
| union |
| { |
| // The input object for local symbols that needs the GOT entry. |
| Mips_relobj<size, big_endian>* object; |
| // If symndx == -1, the global symbol corresponding to this GOT entry. The |
| // symbol's entry is in the local area if mips_sym->global_got_area is |
| // GGA_NONE, otherwise it is in the global area. |
| Mips_symbol<size>* sym; |
| } d; |
| |
| // The TLS type of this GOT entry. An LDM GOT entry will be a local |
| // symbol entry with r_symndx == 0. |
| unsigned char tls_type_; |
| |
| // Whether this is a STT_SECTION symbol. |
| bool is_section_symbol_; |
| |
| // For local GOT entries, section index of the local symbol. |
| unsigned int shndx_; |
| }; |
| |
| // Hash for Mips_got_entry. |
| |
| template<int size, bool big_endian> |
| class Mips_got_entry_hash |
| { |
| public: |
| size_t |
| operator()(Mips_got_entry<size, big_endian>* entry) const |
| { return entry->hash(); } |
| }; |
| |
| // Equality for Mips_got_entry. |
| |
| template<int size, bool big_endian> |
| class Mips_got_entry_eq |
| { |
| public: |
| bool |
| operator()(Mips_got_entry<size, big_endian>* e1, |
| Mips_got_entry<size, big_endian>* e2) const |
| { return e1->equals(e2); } |
| }; |
| |
| // Hash for Mips_symbol. |
| |
| template<int size> |
| class Mips_symbol_hash |
| { |
| public: |
| size_t |
| operator()(Mips_symbol<size>* sym) const |
| { return sym->hash(); } |
| }; |
| |
| // Got_page_range. This class describes a range of addends: [MIN_ADDEND, |
| // MAX_ADDEND]. The instances form a non-overlapping list that is sorted by |
| // increasing MIN_ADDEND. |
| |
| struct Got_page_range |
| { |
| Got_page_range() |
| : next(NULL), min_addend(0), max_addend(0) |
| { } |
| |
| Got_page_range* next; |
| int min_addend; |
| int max_addend; |
| |
| // Return the maximum number of GOT page entries required. |
| int |
| get_max_pages() |
| { return (this->max_addend - this->min_addend + 0x1ffff) >> 16; } |
| }; |
| |
| // Got_page_entry. This class describes the range of addends that are applied |
| // to page relocations against a given symbol. |
| |
| struct Got_page_entry |
| { |
| Got_page_entry() |
| : object(NULL), symndx(-1U), ranges(NULL) |
| { } |
| |
| Got_page_entry(Object* object_, unsigned int symndx_) |
| : object(object_), symndx(symndx_), ranges(NULL) |
| { } |
| |
| // The input object that needs the GOT page entry. |
| Object* object; |
| // The index of the symbol, as stored in the relocation r_info. |
| unsigned int symndx; |
| // The ranges for this page entry. |
| Got_page_range* ranges; |
| }; |
| |
| // Hash for Got_page_entry. |
| |
| struct Got_page_entry_hash |
| { |
| size_t |
| operator()(Got_page_entry* entry) const |
| { return reinterpret_cast<uintptr_t>(entry->object) + entry->symndx; } |
| }; |
| |
| // Equality for Got_page_entry. |
| |
| struct Got_page_entry_eq |
| { |
| bool |
| operator()(Got_page_entry* entry1, Got_page_entry* entry2) const |
| { |
| return entry1->object == entry2->object && entry1->symndx == entry2->symndx; |
| } |
| }; |
| |
| // This class is used to hold .got information when linking. |
| |
| template<int size, bool big_endian> |
| class Mips_got_info |
| { |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Mips_address; |
| typedef Output_data_reloc<elfcpp::SHT_REL, true, size, big_endian> |
| Reloc_section; |
| typedef Unordered_map<unsigned int, unsigned int> Got_page_offsets; |
| |
| // Unordered set of GOT entries. |
| typedef Unordered_set<Mips_got_entry<size, big_endian>*, |
| Mips_got_entry_hash<size, big_endian>, |
| Mips_got_entry_eq<size, big_endian> > Got_entry_set; |
| |
| // Unordered set of GOT page entries. |
| typedef Unordered_set<Got_page_entry*, |
| Got_page_entry_hash, Got_page_entry_eq> Got_page_entry_set; |
| |
| // Unordered set of global GOT entries. |
| typedef Unordered_set<Mips_symbol<size>*, Mips_symbol_hash<size> > |
| Global_got_entry_set; |
| |
| public: |
| Mips_got_info() |
| : local_gotno_(0), page_gotno_(0), global_gotno_(0), reloc_only_gotno_(0), |
| tls_gotno_(0), tls_ldm_offset_(-1U), global_got_symbols_(), |
| got_entries_(), got_page_entries_(), got_page_offset_start_(0), |
| got_page_offset_next_(0), got_page_offsets_(), next_(NULL), index_(-1U), |
| offset_(0) |
| { } |
| |
| // Reserve GOT entry for a GOT relocation of type R_TYPE against symbol |
| // SYMNDX + ADDEND, where SYMNDX is a local symbol in section SHNDX in OBJECT. |
| void |
| record_local_got_symbol(Mips_relobj<size, big_endian>* object, |
| unsigned int symndx, Mips_address addend, |
| unsigned int r_type, unsigned int shndx, |
| bool is_section_symbol); |
| |
| // Reserve GOT entry for a GOT relocation of type R_TYPE against MIPS_SYM, |
| // in OBJECT. FOR_CALL is true if the caller is only interested in |
| // using the GOT entry for calls. DYN_RELOC is true if R_TYPE is a dynamic |
| // relocation. |
| void |
| record_global_got_symbol(Mips_symbol<size>* mips_sym, |
| Mips_relobj<size, big_endian>* object, |
| unsigned int r_type, bool dyn_reloc, bool for_call); |
| |
| // Add ENTRY to master GOT and to OBJECT's GOT. |
| void |
| record_got_entry(Mips_got_entry<size, big_endian>* entry, |
| Mips_relobj<size, big_endian>* object); |
| |
| // Record that OBJECT has a page relocation against symbol SYMNDX and |
| // that ADDEND is the addend for that relocation. |
| void |
| record_got_page_entry(Mips_relobj<size, big_endian>* object, |
| unsigned int symndx, int addend); |
| |
| // Create all entries that should be in the local part of the GOT. |
| void |
| add_local_entries(Target_mips<size, big_endian>* target, Layout* layout); |
| |
| // Create GOT page entries. |
| void |
| add_page_entries(Target_mips<size, big_endian>* target, Layout* layout); |
| |
| // Create global GOT entries, both GGA_NORMAL and GGA_RELOC_ONLY. |
| void |
| add_global_entries(Target_mips<size, big_endian>* target, Layout* layout, |
| unsigned int non_reloc_only_global_gotno); |
| |
| // Create global GOT entries that should be in the GGA_RELOC_ONLY area. |
| void |
| add_reloc_only_entries(Mips_output_data_got<size, big_endian>* got); |
| |
| // Create TLS GOT entries. |
| void |
| add_tls_entries(Target_mips<size, big_endian>* target, Layout* layout); |
| |
| // Decide whether the symbol needs an entry in the global part of the primary |
| // GOT, setting global_got_area accordingly. Count the number of global |
| // symbols that are in the primary GOT only because they have dynamic |
| // relocations R_MIPS_REL32 against them (reloc_only_gotno). |
| void |
| count_got_symbols(Symbol_table* symtab); |
| |
| // Return the offset of GOT page entry for VALUE. |
| unsigned int |
| get_got_page_offset(Mips_address value, |
| Mips_output_data_got<size, big_endian>* got); |
| |
| // Count the number of GOT entries required. |
| void |
| count_got_entries(); |
| |
| // Count the number of GOT entries required by ENTRY. Accumulate the result. |
| void |
| count_got_entry(Mips_got_entry<size, big_endian>* entry); |
| |
| // Add FROM's GOT entries. |
| void |
| add_got_entries(Mips_got_info<size, big_endian>* from); |
| |
| // Add FROM's GOT page entries. |
| void |
| add_got_page_count(Mips_got_info<size, big_endian>* from); |
| |
| // Return GOT size. |
| unsigned int |
| got_size() const |
| { return ((2 + this->local_gotno_ + this->page_gotno_ + this->global_gotno_ |
| + this->tls_gotno_) * size/8); |
| } |
| |
| // Return the number of local GOT entries. |
| unsigned int |
| local_gotno() const |
| { return this->local_gotno_; } |
| |
| // Return the maximum number of page GOT entries needed. |
| unsigned int |
| page_gotno() const |
| { return this->page_gotno_; } |
| |
| // Return the number of global GOT entries. |
| unsigned int |
| global_gotno() const |
| { return this->global_gotno_; } |
| |
| // Set the number of global GOT entries. |
| void |
| set_global_gotno(unsigned int global_gotno) |
| { this->global_gotno_ = global_gotno; } |
| |
| // Return the number of GGA_RELOC_ONLY global GOT entries. |
| unsigned int |
| reloc_only_gotno() const |
| { return this->reloc_only_gotno_; } |
| |
| // Return the number of TLS GOT entries. |
| unsigned int |
| tls_gotno() const |
| { return this->tls_gotno_; } |
| |
| // Return the GOT type for this GOT. Used for multi-GOT links only. |
| unsigned int |
| multigot_got_type(unsigned int got_type) const |
| { |
| switch (got_type) |
| { |
| case GOT_TYPE_STANDARD: |
| return GOT_TYPE_STANDARD_MULTIGOT + this->index_; |
| case GOT_TYPE_TLS_OFFSET: |
| return GOT_TYPE_TLS_OFFSET_MULTIGOT + this->index_; |
| case GOT_TYPE_TLS_PAIR: |
| return GOT_TYPE_TLS_PAIR_MULTIGOT + this->index_; |
| default: |
| gold_unreachable(); |
| } |
| } |
| |
| // Remove lazy-binding stubs for global symbols in this GOT. |
| void |
| remove_lazy_stubs(Target_mips<size, big_endian>* target); |
| |
| // Return offset of this GOT from the start of .got section. |
| unsigned int |
| offset() const |
| { return this->offset_; } |
| |
| // Set offset of this GOT from the start of .got section. |
| void |
| set_offset(unsigned int offset) |
| { this->offset_ = offset; } |
| |
| // Set index of this GOT in multi-GOT links. |
| void |
| set_index(unsigned int index) |
| { this->index_ = index; } |
| |
| // Return next GOT in multi-GOT links. |
| Mips_got_info<size, big_endian>* |
| next() const |
| { return this->next_; } |
| |
| // Set next GOT in multi-GOT links. |
| void |
| set_next(Mips_got_info<size, big_endian>* next) |
| { this->next_ = next; } |
| |
| // Return the offset of TLS LDM entry for this GOT. |
| unsigned int |
| tls_ldm_offset() const |
| { return this->tls_ldm_offset_; } |
| |
| // Set the offset of TLS LDM entry for this GOT. |
| void |
| set_tls_ldm_offset(unsigned int tls_ldm_offset) |
| { this->tls_ldm_offset_ = tls_ldm_offset; } |
| |
| Global_got_entry_set& |
| global_got_symbols() |
| { return this->global_got_symbols_; } |
| |
| // Return the GOT_TLS_* type required by relocation type R_TYPE. |
| static int |
| mips_elf_reloc_tls_type(unsigned int r_type) |
| { |
| if (tls_gd_reloc(r_type)) |
| return GOT_TLS_GD; |
| |
| if (tls_ldm_reloc(r_type)) |
| return GOT_TLS_LDM; |
| |
| if (tls_gottprel_reloc(r_type)) |
| return GOT_TLS_IE; |
| |
| return GOT_TLS_NONE; |
| } |
| |
| // Return the number of GOT slots needed for GOT TLS type TYPE. |
| static int |
| mips_tls_got_entries(unsigned int type) |
| { |
| switch (type) |
| { |
| case GOT_TLS_GD: |
| case GOT_TLS_LDM: |
| return 2; |
| |
| case GOT_TLS_IE: |
| return 1; |
| |
| case GOT_TLS_NONE: |
| return 0; |
| |
| default: |
| gold_unreachable(); |
| } |
| } |
| |
| private: |
| // The number of local GOT entries. |
| unsigned int local_gotno_; |
| // The maximum number of page GOT entries needed. |
| unsigned int page_gotno_; |
| // The number of global GOT entries. |
| unsigned int global_gotno_; |
| // The number of global GOT entries that are in the GGA_RELOC_ONLY area. |
| unsigned int reloc_only_gotno_; |
| // The number of TLS GOT entries. |
| unsigned int tls_gotno_; |
| // The offset of TLS LDM entry for this GOT. |
| unsigned int tls_ldm_offset_; |
| // All symbols that have global GOT entry. |
| Global_got_entry_set global_got_symbols_; |
| // A hash table holding GOT entries. |
| Got_entry_set got_entries_; |
| // A hash table of GOT page entries (only used in master GOT). |
| Got_page_entry_set got_page_entries_; |
| // The offset of first GOT page entry for this GOT. |
| unsigned int got_page_offset_start_; |
| // The offset of next available GOT page entry for this GOT. |
| unsigned int got_page_offset_next_; |
| // A hash table that maps GOT page entry value to the GOT offset where |
| // the entry is located. |
| Got_page_offsets got_page_offsets_; |
| // In multi-GOT links, a pointer to the next GOT. |
| Mips_got_info<size, big_endian>* next_; |
| // Index of this GOT in multi-GOT links. |
| unsigned int index_; |
| // The offset of this GOT in multi-GOT links. |
| unsigned int offset_; |
| }; |
| |
| // This is a helper class used during relocation scan. It records GOT16 addend. |
| |
| template<int size, bool big_endian> |
| struct got16_addend |
| { |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Mips_address; |
| |
| got16_addend(const Sized_relobj_file<size, big_endian>* _object, |
| unsigned int _shndx, unsigned int _r_type, unsigned int _r_sym, |
| Mips_address _addend) |
| : object(_object), shndx(_shndx), r_type(_r_type), r_sym(_r_sym), |
| addend(_addend) |
| { } |
| |
| const Sized_relobj_file<size, big_endian>* object; |
| unsigned int shndx; |
| unsigned int r_type; |
| unsigned int r_sym; |
| Mips_address addend; |
| }; |
| |
| // .MIPS.abiflags section content |
| |
| template<bool big_endian> |
| struct Mips_abiflags |
| { |
| typedef typename elfcpp::Swap<8, big_endian>::Valtype Valtype8; |
| typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype16; |
| typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype32; |
| |
| Mips_abiflags() |
| : version(0), isa_level(0), isa_rev(0), gpr_size(0), cpr1_size(0), |
| cpr2_size(0), fp_abi(0), isa_ext(0), ases(0), flags1(0), flags2(0) |
| { } |
| |
| // Version of flags structure. |
| Valtype16 version; |
| // The level of the ISA: 1-5, 32, 64. |
| Valtype8 isa_level; |
| // The revision of ISA: 0 for MIPS V and below, 1-n otherwise. |
| Valtype8 isa_rev; |
| // The size of general purpose registers. |
| Valtype8 gpr_size; |
| // The size of co-processor 1 registers. |
| Valtype8 cpr1_size; |
| // The size of co-processor 2 registers. |
| Valtype8 cpr2_size; |
| // The floating-point ABI. |
| Valtype8 fp_abi; |
| // Processor-specific extension. |
| Valtype32 isa_ext; |
| // Mask of ASEs used. |
| Valtype32 ases; |
| // Mask of general flags. |
| Valtype32 flags1; |
| Valtype32 flags2; |
| }; |
| |
| // Mips_symbol class. Holds additional symbol information needed for Mips. |
| |
| template<int size> |
| class Mips_symbol : public Sized_symbol<size> |
| { |
| public: |
| Mips_symbol() |
| : need_fn_stub_(false), has_nonpic_branches_(false), la25_stub_offset_(-1U), |
| has_static_relocs_(false), no_lazy_stub_(false), lazy_stub_offset_(0), |
| pointer_equality_needed_(false), global_got_area_(GGA_NONE), |
| global_gotoffset_(-1U), got_only_for_calls_(true), has_lazy_stub_(false), |
| needs_mips_plt_(false), needs_comp_plt_(false), mips_plt_offset_(-1U), |
| comp_plt_offset_(-1U), mips16_fn_stub_(NULL), mips16_call_stub_(NULL), |
| mips16_call_fp_stub_(NULL), applied_secondary_got_fixup_(false) |
| { } |
| |
| // Return whether this is a MIPS16 symbol. |
| bool |
| is_mips16() const |
| { |
| // (st_other & STO_MIPS16) == STO_MIPS16 |
| return ((this->nonvis() & (elfcpp::STO_MIPS16 >> 2)) |
| == elfcpp::STO_MIPS16 >> 2); |
| } |
| |
| // Return whether this is a microMIPS symbol. |
| bool |
| is_micromips() const |
| { |
| // (st_other & STO_MIPS_ISA) == STO_MICROMIPS |
| return ((this->nonvis() & (elfcpp::STO_MIPS_ISA >> 2)) |
| == elfcpp::STO_MICROMIPS >> 2); |
| } |
| |
| // Return whether the symbol needs MIPS16 fn_stub. |
| bool |
| need_fn_stub() const |
| { return this->need_fn_stub_; } |
| |
| // Set that the symbol needs MIPS16 fn_stub. |
| void |
| set_need_fn_stub() |
| { this->need_fn_stub_ = true; } |
| |
| // Return whether this symbol is referenced by branch relocations from |
| // any non-PIC input file. |
| bool |
| has_nonpic_branches() const |
| { return this->has_nonpic_branches_; } |
| |
| // Set that this symbol is referenced by branch relocations from |
| // any non-PIC input file. |
| void |
| set_has_nonpic_branches() |
| { this->has_nonpic_branches_ = true; } |
| |
| // Return the offset of the la25 stub for this symbol from the start of the |
| // la25 stub section. |
| unsigned int |
| la25_stub_offset() const |
| { return this->la25_stub_offset_; } |
| |
| // Set the offset of the la25 stub for this symbol from the start of the |
| // la25 stub section. |
| void |
| set_la25_stub_offset(unsigned int offset) |
| { this->la25_stub_offset_ = offset; } |
| |
| // Return whether the symbol has la25 stub. This is true if this symbol is |
| // for a PIC function, and there are non-PIC branches and jumps to it. |
| bool |
| has_la25_stub() const |
| { return this->la25_stub_offset_ != -1U; } |
| |
| // Return whether there is a relocation against this symbol that must be |
| // resolved by the static linker (that is, the relocation cannot possibly |
| // be made dynamic). |
| bool |
| has_static_relocs() const |
| { return this->has_static_relocs_; } |
| |
| // Set that there is a relocation against this symbol that must be resolved |
| // by the static linker (that is, the relocation cannot possibly be made |
| // dynamic). |
| void |
| set_has_static_relocs() |
| { this->has_static_relocs_ = true; } |
| |
| // Return whether we must not create a lazy-binding stub for this symbol. |
| bool |
| no_lazy_stub() const |
| { return this->no_lazy_stub_; } |
| |
| // Set that we must not create a lazy-binding stub for this symbol. |
| void |
| set_no_lazy_stub() |
| { this->no_lazy_stub_ = true; } |
| |
| // Return the offset of the lazy-binding stub for this symbol from the start |
| // of .MIPS.stubs section. |
| unsigned int |
| lazy_stub_offset() const |
| { return this->lazy_stub_offset_; } |
| |
| // Set the offset of the lazy-binding stub for this symbol from the start |
| // of .MIPS.stubs section. |
| void |
| set_lazy_stub_offset(unsigned int offset) |
| { this->lazy_stub_offset_ = offset; } |
| |
| // Return whether there are any relocations for this symbol where |
| // pointer equality matters. |
| bool |
| pointer_equality_needed() const |
| { return this->pointer_equality_needed_; } |
| |
| // Set that there are relocations for this symbol where pointer equality |
| // matters. |
| void |
| set_pointer_equality_needed() |
| { this->pointer_equality_needed_ = true; } |
| |
| // Return global GOT area where this symbol in located. |
| Global_got_area |
| global_got_area() const |
| { return this->global_got_area_; } |
| |
| // Set global GOT area where this symbol in located. |
| void |
| set_global_got_area(Global_got_area global_got_area) |
| { this->global_got_area_ = global_got_area; } |
| |
| // Return the global GOT offset for this symbol. For multi-GOT links, this |
| // returns the offset from the start of .got section to the first GOT entry |
| // for the symbol. Note that in multi-GOT links the symbol can have entry |
| // in more than one GOT. |
| unsigned int |
| global_gotoffset() const |
| { return this->global_gotoffset_; } |
| |
| // Set the global GOT offset for this symbol. Note that in multi-GOT links |
| // the symbol can have entry in more than one GOT. This method will set |
| // the offset only if it is less than current offset. |
| void |
| set_global_gotoffset(unsigned int offset) |
| { |
| if (this->global_gotoffset_ == -1U || offset < this->global_gotoffset_) |
| this->global_gotoffset_ = offset; |
| } |
| |
| // Return whether all GOT relocations for this symbol are for calls. |
| bool |
| got_only_for_calls() const |
| { return this->got_only_for_calls_; } |
| |
| // Set that there is a GOT relocation for this symbol that is not for call. |
| void |
| set_got_not_only_for_calls() |
| { this->got_only_for_calls_ = false; } |
| |
| // Return whether this is a PIC symbol. |
| bool |
| is_pic() const |
| { |
| // (st_other & STO_MIPS_FLAGS) == STO_MIPS_PIC |
| return ((this->nonvis() & (elfcpp::STO_MIPS_FLAGS >> 2)) |
| == (elfcpp::STO_MIPS_PIC >> 2)); |
| } |
| |
| // Set the flag in st_other field that marks this symbol as PIC. |
| void |
| set_pic() |
| { |
| if (this->is_mips16()) |
| // (st_other & ~(STO_MIPS16 | STO_MIPS_FLAGS)) | STO_MIPS_PIC |
| this->set_nonvis((this->nonvis() |
| & ~((elfcpp::STO_MIPS16 >> 2) |
| | (elfcpp::STO_MIPS_FLAGS >> 2))) |
| | (elfcpp::STO_MIPS_PIC >> 2)); |
| else |
| // (other & ~STO_MIPS_FLAGS) | STO_MIPS_PIC |
| this->set_nonvis((this->nonvis() & ~(elfcpp::STO_MIPS_FLAGS >> 2)) |
| | (elfcpp::STO_MIPS_PIC >> 2)); |
| } |
| |
| // Set the flag in st_other field that marks this symbol as PLT. |
| void |
| set_mips_plt() |
| { |
| if (this->is_mips16()) |
| // (st_other & (STO_MIPS16 | ~STO_MIPS_FLAGS)) | STO_MIPS_PLT |
| this->set_nonvis((this->nonvis() |
| & ((elfcpp::STO_MIPS16 >> 2) |
| | ~(elfcpp::STO_MIPS_FLAGS >> 2))) |
| | (elfcpp::STO_MIPS_PLT >> 2)); |
| |
| else |
| // (st_other & ~STO_MIPS_FLAGS) | STO_MIPS_PLT |
| this->set_nonvis((this->nonvis() & ~(elfcpp::STO_MIPS_FLAGS >> 2)) |
| | (elfcpp::STO_MIPS_PLT >> 2)); |
| } |
| |
| // Downcast a base pointer to a Mips_symbol pointer. |
| static Mips_symbol<size>* |
| as_mips_sym(Symbol* sym) |
| { return static_cast<Mips_symbol<size>*>(sym); } |
| |
| // Downcast a base pointer to a Mips_symbol pointer. |
| static const Mips_symbol<size>* |
| as_mips_sym(const Symbol* sym) |
| { return static_cast<const Mips_symbol<size>*>(sym); } |
| |
| // Return whether the symbol has lazy-binding stub. |
| bool |
| has_lazy_stub() const |
| { return this->has_lazy_stub_; } |
| |
| // Set whether the symbol has lazy-binding stub. |
| void |
| set_has_lazy_stub(bool has_lazy_stub) |
| { this->has_lazy_stub_ = has_lazy_stub; } |
| |
| // Return whether the symbol needs a standard PLT entry. |
| bool |
| needs_mips_plt() const |
| { return this->needs_mips_plt_; } |
| |
| // Set whether the symbol needs a standard PLT entry. |
| void |
| set_needs_mips_plt(bool needs_mips_plt) |
| { this->needs_mips_plt_ = needs_mips_plt; } |
| |
| // Return whether the symbol needs a compressed (MIPS16 or microMIPS) PLT |
| // entry. |
| bool |
| needs_comp_plt() const |
| { return this->needs_comp_plt_; } |
| |
| // Set whether the symbol needs a compressed (MIPS16 or microMIPS) PLT entry. |
| void |
| set_needs_comp_plt(bool needs_comp_plt) |
| { this->needs_comp_plt_ = needs_comp_plt; } |
| |
| // Return standard PLT entry offset, or -1 if none. |
| unsigned int |
| mips_plt_offset() const |
| { return this->mips_plt_offset_; } |
| |
| // Set standard PLT entry offset. |
| void |
| set_mips_plt_offset(unsigned int mips_plt_offset) |
| { this->mips_plt_offset_ = mips_plt_offset; } |
| |
| // Return whether the symbol has standard PLT entry. |
| bool |
| has_mips_plt_offset() const |
| { return this->mips_plt_offset_ != -1U; } |
| |
| // Return compressed (MIPS16 or microMIPS) PLT entry offset, or -1 if none. |
| unsigned int |
| comp_plt_offset() const |
| { return this->comp_plt_offset_; } |
| |
| // Set compressed (MIPS16 or microMIPS) PLT entry offset. |
| void |
| set_comp_plt_offset(unsigned int comp_plt_offset) |
| { this->comp_plt_offset_ = comp_plt_offset; } |
| |
| // Return whether the symbol has compressed (MIPS16 or microMIPS) PLT entry. |
| bool |
| has_comp_plt_offset() const |
| { return this->comp_plt_offset_ != -1U; } |
| |
| // Return MIPS16 fn stub for a symbol. |
| template<bool big_endian> |
| Mips16_stub_section<size, big_endian>* |
| get_mips16_fn_stub() const |
| { |
| return static_cast<Mips16_stub_section<size, big_endian>*>(mips16_fn_stub_); |
| } |
| |
| // Set MIPS16 fn stub for a symbol. |
| void |
| set_mips16_fn_stub(Mips16_stub_section_base* stub) |
| { this->mips16_fn_stub_ = stub; } |
| |
| // Return whether symbol has MIPS16 fn stub. |
| bool |
| has_mips16_fn_stub() const |
| { return this->mips16_fn_stub_ != NULL; } |
| |
| // Return MIPS16 call stub for a symbol. |
| template<bool big_endian> |
| Mips16_stub_section<size, big_endian>* |
| get_mips16_call_stub() const |
| { |
| return static_cast<Mips16_stub_section<size, big_endian>*>( |
| mips16_call_stub_); |
| } |
| |
| // Set MIPS16 call stub for a symbol. |
| void |
| set_mips16_call_stub(Mips16_stub_section_base* stub) |
| { this->mips16_call_stub_ = stub; } |
| |
| // Return whether symbol has MIPS16 call stub. |
| bool |
| has_mips16_call_stub() const |
| { return this->mips16_call_stub_ != NULL; } |
| |
| // Return MIPS16 call_fp stub for a symbol. |
| template<bool big_endian> |
| Mips16_stub_section<size, big_endian>* |
| get_mips16_call_fp_stub() const |
| { |
| return static_cast<Mips16_stub_section<size, big_endian>*>( |
| mips16_call_fp_stub_); |
| } |
| |
| // Set MIPS16 call_fp stub for a symbol. |
| void |
| set_mips16_call_fp_stub(Mips16_stub_section_base* stub) |
| { this->mips16_call_fp_stub_ = stub; } |
| |
| // Return whether symbol has MIPS16 call_fp stub. |
| bool |
| has_mips16_call_fp_stub() const |
| { return this->mips16_call_fp_stub_ != NULL; } |
| |
| bool |
| get_applied_secondary_got_fixup() const |
| { return applied_secondary_got_fixup_; } |
| |
| void |
| set_applied_secondary_got_fixup() |
| { this->applied_secondary_got_fixup_ = true; } |
| |
| // Return the hash of this symbol. |
| size_t |
| hash() const |
| { |
| return gold::string_hash<char>(this->name()); |
| } |
| |
| private: |
| // Whether the symbol needs MIPS16 fn_stub. This is true if this symbol |
| // appears in any relocs other than a 16 bit call. |
| bool need_fn_stub_; |
| |
| // True if this symbol is referenced by branch relocations from |
| // any non-PIC input file. This is used to determine whether an |
| // la25 stub is required. |
| bool has_nonpic_branches_; |
| |
| // The offset of the la25 stub for this symbol from the start of the |
| // la25 stub section. |
| unsigned int la25_stub_offset_; |
| |
| // True if there is a relocation against this symbol that must be |
| // resolved by the static linker (that is, the relocation cannot |
| // possibly be made dynamic). |
| bool has_static_relocs_; |
| |
| // Whether we must not create a lazy-binding stub for this symbol. |
| // This is true if the symbol has relocations related to taking the |
| // function's address. |
| bool no_lazy_stub_; |
| |
| // The offset of the lazy-binding stub for this symbol from the start of |
| // .MIPS.stubs section. |
| unsigned int lazy_stub_offset_; |
| |
| // True if there are any relocations for this symbol where pointer equality |
| // matters. |
| bool pointer_equality_needed_; |
| |
| // Global GOT area where this symbol in located, or GGA_NONE if symbol is not |
| // in the global part of the GOT. |
| Global_got_area global_got_area_; |
| |
| // The global GOT offset for this symbol. For multi-GOT links, this is offset |
| // from the start of .got section to the first GOT entry for the symbol. |
| // Note that in multi-GOT links the symbol can have entry in more than one GOT. |
| unsigned int global_gotoffset_; |
| |
| // Whether all GOT relocations for this symbol are for calls. |
| bool got_only_for_calls_; |
| // Whether the symbol has lazy-binding stub. |
| bool has_lazy_stub_; |
| // Whether the symbol needs a standard PLT entry. |
| bool needs_mips_plt_; |
| // Whether the symbol needs a compressed (MIPS16 or microMIPS) PLT entry. |
| bool needs_comp_plt_; |
| // Standard PLT entry offset, or -1 if none. |
| unsigned int mips_plt_offset_; |
| // Compressed (MIPS16 or microMIPS) PLT entry offset, or -1 if none. |
| unsigned int comp_plt_offset_; |
| // MIPS16 fn stub for a symbol. |
| Mips16_stub_section_base* mips16_fn_stub_; |
| // MIPS16 call stub for a symbol. |
| Mips16_stub_section_base* mips16_call_stub_; |
| // MIPS16 call_fp stub for a symbol. |
| Mips16_stub_section_base* mips16_call_fp_stub_; |
| |
| bool applied_secondary_got_fixup_; |
| }; |
| |
| // Mips16_stub_section class. |
| |
| // The mips16 compiler uses a couple of special sections to handle |
| // floating point arguments. |
| |
| // Section names that look like .mips16.fn.FNNAME contain stubs that |
| // copy floating point arguments from the fp regs to the gp regs and |
| // then jump to FNNAME. If any 32 bit function calls FNNAME, the |
| // call should be redirected to the stub instead. If no 32 bit |
| // function calls FNNAME, the stub should be discarded. We need to |
| // consider any reference to the function, not just a call, because |
| // if the address of the function is taken we will need the stub, |
| // since the address might be passed to a 32 bit function. |
| |
| // Section names that look like .mips16.call.FNNAME contain stubs |
| // that copy floating point arguments from the gp regs to the fp |
| // regs and then jump to FNNAME. If FNNAME is a 32 bit function, |
| // then any 16 bit function that calls FNNAME should be redirected |
| // to the stub instead. If FNNAME is not a 32 bit function, the |
| // stub should be discarded. |
| |
| // .mips16.call.fp.FNNAME sections are similar, but contain stubs |
| // which call FNNAME and then copy the return value from the fp regs |
| // to the gp regs. These stubs store the return address in $18 while |
| // calling FNNAME; any function which might call one of these stubs |
| // must arrange to save $18 around the call. (This case is not |
| // needed for 32 bit functions that call 16 bit functions, because |
| // 16 bit functions always return floating point values in both |
| // $f0/$f1 and $2/$3.) |
| |
| // Note that in all cases FNNAME might be defined statically. |
| // Therefore, FNNAME is not used literally. Instead, the relocation |
| // information will indicate which symbol the section is for. |
| |
| // We record any stubs that we find in the symbol table. |
| |
| // TODO(sasa): All mips16 stub sections should be emitted in the .text section. |
| |
| class Mips16_stub_section_base { }; |
| |
| template<int size, bool big_endian> |
| class Mips16_stub_section : public Mips16_stub_section_base |
| { |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Mips_address; |
| |
| public: |
| Mips16_stub_section(Mips_relobj<size, big_endian>* object, unsigned int shndx) |
| : object_(object), shndx_(shndx), r_sym_(0), gsym_(NULL), |
| found_r_mips_none_(false) |
| { |
| gold_assert(object->is_mips16_fn_stub_section(shndx) |
| || object->is_mips16_call_stub_section(shndx) |
| || object->is_mips16_call_fp_stub_section(shndx)); |
| } |
| |
| // Return the object of this stub section. |
| Mips_relobj<size, big_endian>* |
| object() const |
| { return this->object_; } |
| |
| // Return the size of a section. |
| uint64_t |
| section_size() const |
| { return this->object_->section_size(this->shndx_); } |
| |
| // Return section index of this stub section. |
| unsigned int |
| shndx() const |
| { return this->shndx_; } |
| |
| // Return symbol index, if stub is for a local function. |
| unsigned int |
| r_sym() const |
| { return this->r_sym_; } |
| |
| // Return symbol, if stub is for a global function. |
| Mips_symbol<size>* |
| gsym() const |
| { return this->gsym_; } |
| |
| // Return whether stub is for a local function. |
| bool |
| is_for_local_function() const |
| { return this->gsym_ == NULL; } |
| |
| // This method is called when a new relocation R_TYPE for local symbol R_SYM |
| // is found in the stub section. Try to find stub target. |
| void |
| new_local_reloc_found(unsigned int r_type, unsigned int r_sym) |
| { |
| // To find target symbol for this stub, trust the first R_MIPS_NONE |
| // relocation, if any. Otherwise trust the first relocation, whatever |
| // its kind. |
| if (this->found_r_mips_none_) |
| return; |
| if (r_type == elfcpp::R_MIPS_NONE) |
| { |
| this->r_sym_ = r_sym; |
| this->gsym_ = NULL; |
| this->found_r_mips_none_ = true; |
| } |
| else if (!is_target_found()) |
| this->r_sym_ = r_sym; |
| } |
| |
| // This method is called when a new relocation R_TYPE for global symbol GSYM |
| // is found in the stub section. Try to find stub target. |
| void |
| new_global_reloc_found(unsigned int r_type, Mips_symbol<size>* gsym) |
| { |
| // To find target symbol for this stub, trust the first R_MIPS_NONE |
| // relocation, if any. Otherwise trust the first relocation, whatever |
| // its kind. |
| if (this->found_r_mips_none_) |
| return; |
| if (r_type == elfcpp::R_MIPS_NONE) |
| { |
| this->gsym_ = gsym; |
| this->r_sym_ = 0; |
| this->found_r_mips_none_ = true; |
| } |
| else if (!is_target_found()) |
| this->gsym_ = gsym; |
| } |
| |
| // Return whether we found the stub target. |
| bool |
| is_target_found() const |
| { return this->r_sym_ != 0 || this->gsym_ != NULL; } |
| |
| // Return whether this is a fn stub. |
| bool |
| is_fn_stub() const |
| { return this->object_->is_mips16_fn_stub_section(this->shndx_); } |
| |
| // Return whether this is a call stub. |
| bool |
| is_call_stub() const |
| { return this->object_->is_mips16_call_stub_section(this->shndx_); } |
| |
| // Return whether this is a call_fp stub. |
| bool |
| is_call_fp_stub() const |
| { return this->object_->is_mips16_call_fp_stub_section(this->shndx_); } |
| |
| // Return the output address. |
| Mips_address |
| output_address() const |
| { |
| return (this->object_->output_section(this->shndx_)->address() |
| + this->object_->output_section_offset(this->shndx_)); |
| } |
| |
| private: |
| // The object of this stub section. |
| Mips_relobj<size, big_endian>* object_; |
| // The section index of this stub section. |
| unsigned int shndx_; |
| // The symbol index, if stub is for a local function. |
| unsigned int r_sym_; |
| // The symbol, if stub is for a global function. |
| Mips_symbol<size>* gsym_; |
| // True if we found R_MIPS_NONE relocation in this stub. |
| bool found_r_mips_none_; |
| }; |
| |
| // Mips_relobj class. |
| |
| template<int size, bool big_endian> |
| class Mips_relobj : public Sized_relobj_file<size, big_endian> |
| { |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Mips_address; |
| typedef std::map<unsigned int, Mips16_stub_section<size, big_endian>*> |
| Mips16_stubs_int_map; |
| typedef typename elfcpp::Swap<size, big_endian>::Valtype Valtype; |
| |
| public: |
| Mips_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), |
| processor_specific_flags_(0), local_symbol_is_mips16_(), |
| local_symbol_is_micromips_(), mips16_stub_sections_(), |
| local_non_16bit_calls_(), local_16bit_calls_(), local_mips16_fn_stubs_(), |
| local_mips16_call_stubs_(), gp_(0), has_reginfo_section_(false), |
| merge_processor_specific_data_(true), got_info_(NULL), |
| section_is_mips16_fn_stub_(), section_is_mips16_call_stub_(), |
| section_is_mips16_call_fp_stub_(), pdr_shndx_(-1U), |
| attributes_section_data_(NULL), abiflags_(NULL), gprmask_(0), |
| cprmask1_(0), cprmask2_(0), cprmask3_(0), cprmask4_(0) |
| { |
| this->is_pic_ = (ehdr.get_e_flags() & elfcpp::EF_MIPS_PIC) != 0; |
| this->is_n32_ = elfcpp::abi_n32(ehdr.get_e_flags()); |
| } |
| |
| ~Mips_relobj() |
| { delete this->attributes_section_data_; } |
| |
| // Downcast a base pointer to a Mips_relobj pointer. This is |
| // not type-safe but we only use Mips_relobj not the base class. |
| static Mips_relobj<size, big_endian>* |
| as_mips_relobj(Relobj* relobj) |
| { return static_cast<Mips_relobj<size, big_endian>*>(relobj); } |
| |
| // Downcast a base pointer to a Mips_relobj pointer. This is |
| // not type-safe but we only use Mips_relobj not the base class. |
| static const Mips_relobj<size, big_endian>* |
| as_mips_relobj(const Relobj* relobj) |
| { return static_cast<const Mips_relobj<size, big_endian>*>(relobj); } |
| |
| // Processor-specific flags in ELF file header. This is valid only after |
| // reading symbols. |
| elfcpp::Elf_Word |
| processor_specific_flags() const |
| { return this->processor_specific_flags_; } |
| |
| // Whether a local symbol is MIPS16 symbol. R_SYM is the symbol table |
| // index. This is only valid after do_count_local_symbol is called. |
| bool |
| local_symbol_is_mips16(unsigned int r_sym) const |
| { |
| gold_assert(r_sym < this->local_symbol_is_mips16_.size()); |
| return this->local_symbol_is_mips16_[r_sym]; |
| } |
| |
| // Whether a local symbol is microMIPS symbol. R_SYM is the symbol table |
| // index. This is only valid after do_count_local_symbol is called. |
| bool |
| local_symbol_is_micromips(unsigned int r_sym) const |
| { |
| gold_assert(r_sym < this->local_symbol_is_micromips_.size()); |
| return this->local_symbol_is_micromips_[r_sym]; |
| } |
| |
| // Get or create MIPS16 stub section. |
| Mips16_stub_section<size, big_endian>* |
| get_mips16_stub_section(unsigned int shndx) |
| { |
| typename Mips16_stubs_int_map::const_iterator it = |
| this->mips16_stub_sections_.find(shndx); |
| if (it != this->mips16_stub_sections_.end()) |
| return (*it).second; |
| |
| Mips16_stub_section<size, big_endian>* stub_section = |
| new Mips16_stub_section<size, big_endian>(this, shndx); |
| this->mips16_stub_sections_.insert( |
| std::pair<unsigned int, Mips16_stub_section<size, big_endian>*>( |
| stub_section->shndx(), stub_section)); |
| return stub_section; |
| } |
| |
| // Return MIPS16 fn stub section for local symbol R_SYM, or NULL if this |
| // object doesn't have fn stub for R_SYM. |
| Mips16_stub_section<size, big_endian>* |
| get_local_mips16_fn_stub(unsigned int r_sym) const |
| { |
| typename Mips16_stubs_int_map::const_iterator it = |
| this->local_mips16_fn_stubs_.find(r_sym); |
| if (it != this->local_mips16_fn_stubs_.end()) |
| return (*it).second; |
| return NULL; |
| } |
| |
| // Record that this object has MIPS16 fn stub for local symbol. This method |
| // is only called if we decided not to discard the stub. |
| void |
| add_local_mips16_fn_stub(Mips16_stub_section<size, big_endian>* stub) |
| { |
| gold_assert(stub->is_for_local_function()); |
| unsigned int r_sym = stub->r_sym(); |
| this->local_mips16_fn_stubs_.insert( |
| std::pair<unsigned int, Mips16_stub_section<size, big_endian>*>( |
| r_sym, stub)); |
| } |
| |
| // Return MIPS16 call stub section for local symbol R_SYM, or NULL if this |
| // object doesn't have call stub for R_SYM. |
| Mips16_stub_section<size, big_endian>* |
| get_local_mips16_call_stub(unsigned int r_sym) const |
| { |
| typename Mips16_stubs_int_map::const_iterator it = |
| this->local_mips16_call_stubs_.find(r_sym); |
| if (it != this->local_mips16_call_stubs_.end()) |
| return (*it).second; |
| return NULL; |
| } |
| |
| // Record that this object has MIPS16 call stub for local symbol. This method |
| // is only called if we decided not to discard the stub. |
| void |
| add_local_mips16_call_stub(Mips16_stub_section<size, big_endian>* stub) |
| { |
| gold_assert(stub->is_for_local_function()); |
| unsigned int r_sym = stub->r_sym(); |
| this->local_mips16_call_stubs_.insert( |
| std::pair<unsigned int, Mips16_stub_section<size, big_endian>*>( |
| r_sym, stub)); |
| } |
| |
| // Record that we found "non 16-bit" call relocation against local symbol |
| // SYMNDX. This reloc would need to refer to a MIPS16 fn stub, if there |
| // is one. |
| void |
| add_local_non_16bit_call(unsigned int symndx) |
| { this->local_non_16bit_calls_.insert(symndx); } |
| |
| // Return true if there is any "non 16-bit" call relocation against local |
| // symbol SYMNDX in this object. |
| bool |
| has_local_non_16bit_call_relocs(unsigned int symndx) |
| { |
| return (this->local_non_16bit_calls_.find(symndx) |
| != this->local_non_16bit_calls_.end()); |
| } |
| |
| // Record that we found 16-bit call relocation R_MIPS16_26 against local |
| // symbol SYMNDX. Local MIPS16 call or call_fp stubs will only be needed |
| // if there is some R_MIPS16_26 relocation that refers to the stub symbol. |
| void |
| add_local_16bit_call(unsigned int symndx) |
| { this->local_16bit_calls_.insert(symndx); } |
| |
| // Return true if there is any 16-bit call relocation R_MIPS16_26 against local |
| // symbol SYMNDX in this object. |
| bool |
| has_local_16bit_call_relocs(unsigned int symndx) |
| { |
| return (this->local_16bit_calls_.find(symndx) |
| != this->local_16bit_calls_.end()); |
| } |
| |
| // Get gp value that was used to create this object. |
| Mips_address |
| gp_value() const |
| { return this->gp_; } |
| |
| // Return whether the object is a PIC object. |
| bool |
| is_pic() const |
| { return this->is_pic_; } |
| |
| // Return whether the object uses N32 ABI. |
| bool |
| is_n32() const |
| { return this->is_n32_; } |
| |
| // Return whether the object uses N64 ABI. |
| bool |
| is_n64() const |
| { return size == 64; } |
| |
| // Return whether the object uses NewABI conventions. |
| bool |
| is_newabi() const |
| { return this->is_n32() || this->is_n64(); } |
| |
| // Return Mips_got_info for this object. |
| Mips_got_info<size, big_endian>* |
| get_got_info() const |
| { return this->got_info_; } |
| |
| // Return Mips_got_info for this object. Create new info if it doesn't exist. |
| Mips_got_info<size, big_endian>* |
| get_or_create_got_info() |
| { |
| if (!this->got_info_) |
| this->got_info_ = new Mips_got_info<size, big_endian>(); |
| return this->got_info_; |
| } |
| |
| // Set Mips_got_info for this object. |
| void |
| set_got_info(Mips_got_info<size, big_endian>* got_info) |
| { this->got_info_ = got_info; } |
| |
| // Whether a section SHDNX is a MIPS16 stub section. This is only valid |
| // after do_read_symbols is called. |
| bool |
| is_mips16_stub_section(unsigned int shndx) |
| { |
| return (is_mips16_fn_stub_section(shndx) |
| || is_mips16_call_stub_section(shndx) |
| || is_mips16_call_fp_stub_section(shndx)); |
| } |
| |
| // Return TRUE if relocations in section SHNDX can refer directly to a |
| // MIPS16 function rather than to a hard-float stub. This is only valid |
| // after do_read_symbols is called. |
| bool |
| section_allows_mips16_refs(unsigned int shndx) |
| { |
| return (this->is_mips16_stub_section(shndx) || shndx == this->pdr_shndx_); |
| } |
| |
| // Whether a section SHDNX is a MIPS16 fn stub section. This is only valid |
| // after do_read_symbols is called. |
| bool |
| is_mips16_fn_stub_section(unsigned int shndx) |
| { |
| gold_assert(shndx < this->section_is_mips16_fn_stub_.size()); |
| return this->section_is_mips16_fn_stub_[shndx]; |
| } |
| |
| // Whether a section SHDNX is a MIPS16 call stub section. This is only valid |
| // after do_read_symbols is called. |
| bool |
| is_mips16_call_stub_section(unsigned int shndx) |
| { |
| gold_assert(shndx < this->section_is_mips16_call_stub_.size()); |
| return this->section_is_mips16_call_stub_[shndx]; |
| } |
| |
| // Whether a section SHDNX is a MIPS16 call_fp stub section. This is only |
| // valid after do_read_symbols is called. |
| bool |
| is_mips16_call_fp_stub_section(unsigned int shndx) |
| { |
| gold_assert(shndx < this->section_is_mips16_call_fp_stub_.size()); |
| return this->section_is_mips16_call_fp_stub_[shndx]; |
| } |
| |
| // Discard MIPS16 stub secions that are not needed. |
| void |
| discard_mips16_stub_sections(Symbol_table* symtab); |
| |
| // Return whether there is a .reginfo section. |
| bool |
| has_reginfo_section() const |
| { return this->has_reginfo_section_; } |
| |
| // Return whether we want to merge processor-specific data. |
| bool |
| merge_processor_specific_data() const |
| { return this->merge_processor_specific_data_; } |
| |
| // Return gprmask from the .reginfo section of this object. |
| Valtype |
| gprmask() const |
| { return this->gprmask_; } |
| |
| // Return cprmask1 from the .reginfo section of this object. |
| Valtype |
| cprmask1() const |
| { return this->cprmask1_; } |
| |
| // Return cprmask2 from the .reginfo section of this object. |
| Valtype |
| cprmask2() const |
| { return this->cprmask2_; } |
| |
| // Return cprmask3 from the .reginfo section of this object. |
| Valtype |
| cprmask3() const |
| { return this->cprmask3_; } |
| |
| // Return cprmask4 from the .reginfo section of this object. |
| Valtype |
| cprmask4() const |
| { return this->cprmask4_; } |
| |
| // This is the contents of the .MIPS.abiflags section if there is one. |
| Mips_abiflags<big_endian>* |
| abiflags() |
| { return this->abiflags_; } |
| |
| // This is the contents of the .gnu.attribute section if there is one. |
| const Attributes_section_data* |
| attributes_section_data() const |
| { return this->attributes_section_data_; } |
| |
| protected: |
| // Count the local symbols. |
| void |
| do_count_local_symbols(Stringpool_template<char>*, |
| Stringpool_template<char>*); |
| |
| // Read the symbol information. |
| void |
| do_read_symbols(Read_symbols_data* sd); |
| |
| private: |
| // The name of the options section. |
| const char* mips_elf_options_section_name() |
| { return this->is_newabi() ? ".MIPS.options" : ".options"; } |
| |
| // processor-specific flags in ELF file header. |
| elfcpp::Elf_Word processor_specific_flags_; |
| |
| // Bit vector to tell if a local symbol is a MIPS16 symbol or not. |
| // This is only valid after do_count_local_symbol is called. |
| std::vector<bool> local_symbol_is_mips16_; |
| |
| // Bit vector to tell if a local symbol is a microMIPS symbol or not. |
| // This is only valid after do_count_local_symbol is called. |
| std::vector<bool> local_symbol_is_micromips_; |
| |
| // Map from section index to the MIPS16 stub for that section. This contains |
| // all stubs found in this object. |
| Mips16_stubs_int_map mips16_stub_sections_; |
| |
| // Local symbols that have "non 16-bit" call relocation. This relocation |
| // would need to refer to a MIPS16 fn stub, if there is one. |
| std::set<unsigned int> local_non_16bit_calls_; |
| |
| // Local symbols that have 16-bit call relocation R_MIPS16_26. Local MIPS16 |
| // call or call_fp stubs will only be needed if there is some R_MIPS16_26 |
| // relocation that refers to the stub symbol. |
| std::set<unsigned int> local_16bit_calls_; |
| |
| // Map from local symbol index to the MIPS16 fn stub for that symbol. |
| // This contains only the stubs that we decided not to discard. |
| Mips16_stubs_int_map local_mips16_fn_stubs_; |
| |
| // Map from local symbol index to the MIPS16 call stub for that symbol. |
| // This contains only the stubs that we decided not to discard. |
| Mips16_stubs_int_map local_mips16_call_stubs_; |
| |
| // gp value that was used to create this object. |
| Mips_address gp_; |
| // Whether the object is a PIC object. |
| bool is_pic_ : 1; |
| // Whether the object uses N32 ABI. |
| bool is_n32_ : 1; |
| // Whether the object contains a .reginfo section. |
| bool has_reginfo_section_ : 1; |
| // Whether we merge processor-specific data of this object to output. |
| bool merge_processor_specific_data_ : 1; |
| // The Mips_got_info for this object. |
| Mips_got_info<size, big_endian>* got_info_; |
| |
| // Bit vector to tell if a section is a MIPS16 fn stub section or not. |
| // This is only valid after do_read_symbols is called. |
| std::vector<bool> section_is_mips16_fn_stub_; |
| |
| // Bit vector to tell if a section is a MIPS16 call stub section or not. |
| // This is only valid after do_read_symbols is called. |
| std::vector<bool> section_is_mips16_call_stub_; |
| |
| // Bit vector to tell if a section is a MIPS16 call_fp stub section or not. |
| // This is only valid after do_read_symbols is called. |
| std::vector<bool> section_is_mips16_call_fp_stub_; |
| |
| // .pdr section index. |
| unsigned int pdr_shndx_; |
| |
| // Object attributes if there is a .gnu.attributes section or NULL. |
| Attributes_section_data* attributes_section_data_; |
| |
| // Object abiflags if there is a .MIPS.abiflags section or NULL. |
| Mips_abiflags<big_endian>* abiflags_; |
| |
| // gprmask from the .reginfo section of this object. |
| Valtype gprmask_; |
| // cprmask1 from the .reginfo section of this object. |
| Valtype cprmask1_; |
| // cprmask2 from the .reginfo section of this object. |
| Valtype cprmask2_; |
| // cprmask3 from the .reginfo section of this object. |
| Valtype cprmask3_; |
| // cprmask4 from the .reginfo section of this object. |
| Valtype cprmask4_; |
| }; |
| |
| // Mips_output_data_got class. |
| |
| template<int size, bool big_endian> |
| class Mips_output_data_got : public Output_data_got<size, big_endian> |
| { |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Mips_address; |
| typedef Output_data_reloc<elfcpp::SHT_REL, true, size, big_endian> |
| Reloc_section; |
| typedef typename elfcpp::Swap<size, big_endian>::Valtype Valtype; |
| |
| public: |
| Mips_output_data_got(Target_mips<size, big_endian>* target, |
| Symbol_table* symtab, Layout* layout) |
| : Output_data_got<size, big_endian>(), target_(target), |
| symbol_table_(symtab), layout_(layout), static_relocs_(), got_view_(NULL), |
| first_global_got_dynsym_index_(-1U), primary_got_(NULL), |
| secondary_got_relocs_() |
| { |
| this->master_got_info_ = new Mips_got_info<size, big_endian>(); |
| this->set_addralign(16); |
| } |
| |
| // Reserve GOT entry for a GOT relocation of type R_TYPE against symbol |
| // SYMNDX + ADDEND, where SYMNDX is a local symbol in section SHNDX in OBJECT. |
| void |
| record_local_got_symbol(Mips_relobj<size, big_endian>* object, |
| unsigned int symndx, Mips_address addend, |
| unsigned int r_type, unsigned int shndx, |
| bool is_section_symbol) |
| { |
| this->master_got_info_->record_local_got_symbol(object, symndx, addend, |
| r_type, shndx, |
| is_section_symbol); |
| } |
| |
| // Reserve GOT entry for a GOT relocation of type R_TYPE against MIPS_SYM, |
| // in OBJECT. FOR_CALL is true if the caller is only interested in |
| // using the GOT entry for calls. DYN_RELOC is true if R_TYPE is a dynamic |
| // relocation. |
| void |
| record_global_got_symbol(Mips_symbol<size>* mips_sym, |
| Mips_relobj<size, big_endian>* object, |
| unsigned int r_type, bool dyn_reloc, bool for_call) |
| { |
| this->master_got_info_->record_global_got_symbol(mips_sym, object, r_type, |
| dyn_reloc, for_call); |
| } |
| |
| // Record that OBJECT has a page relocation against symbol SYMNDX and |
| // that ADDEND is the addend for that relocation. |
| void |
| record_got_page_entry(Mips_relobj<size, big_endian>* object, |
| unsigned int symndx, int addend) |
| { this->master_got_info_->record_got_page_entry(object, symndx, addend); } |
| |
| // Add a static entry for the GOT entry at OFFSET. GSYM is a global |
| // symbol and R_TYPE is the code of a dynamic relocation that needs to be |
| // applied in a static link. |
| void |
| add_static_reloc(unsigned int got_offset, unsigned int r_type, |
| Mips_symbol<size>* gsym) |
| { this->static_relocs_.push_back(Static_reloc(got_offset, r_type, gsym)); } |
| |
| // Add a static reloc for the GOT entry at OFFSET. RELOBJ is an object |
| // defining a local symbol with INDEX. R_TYPE is the code of a dynamic |
| // relocation that needs to be applied in a static link. |
| void |
| add_static_reloc(unsigned int got_offset, unsigned int r_type, |
| Sized_relobj_file<size, big_endian>* relobj, |
| unsigned int index) |
| { |
| this->static_relocs_.push_back(Static_reloc(got_offset, r_type, relobj, |
| index)); |
| } |
| |
| // Record that global symbol GSYM has R_TYPE dynamic relocation in the |
| // secondary GOT at OFFSET. |
| void |
| add_secondary_got_reloc(unsigned int got_offset, unsigned int r_type, |
| Mips_symbol<size>* gsym) |
| { |
| this->secondary_got_relocs_.push_back(Static_reloc(got_offset, |
| r_type, gsym)); |
| } |
| |
| // Update GOT entry at OFFSET with VALUE. |
| void |
| update_got_entry(unsigned int offset, Mips_address value) |
| { |
| elfcpp::Swap<size, big_endian>::writeval(this->got_view_ + offset, value); |
| } |
| |
| // Return the number of entries in local part of the GOT. This includes |
| // local entries, page entries and 2 reserved entries. |
| unsigned int |
| get_local_gotno() const |
| { |
| if (!this->multi_got()) |
| { |
| return (2 + this->master_got_info_->local_gotno() |
| + this->master_got_info_->page_gotno()); |
| } |
| else |
| return 2 + this->primary_got_->local_gotno() + this->primary_got_->page_gotno(); |
| } |
| |
| // Return dynamic symbol table index of the first symbol with global GOT |
| // entry. |
| unsigned int |
| first_global_got_dynsym_index() const |
| { return this->first_global_got_dynsym_index_; } |
| |
| // Set dynamic symbol table index of the first symbol with global GOT entry. |
| void |
| set_first_global_got_dynsym_index(unsigned int index) |
| { this->first_global_got_dynsym_index_ = index; } |
| |
| // Lay out the GOT. Add local, global and TLS entries. If GOT is |
| // larger than 64K, create multi-GOT. |
| void |
| lay_out_got(Layout* layout, Symbol_table* symtab, |
| const Input_objects* input_objects); |
| |
| // Create multi-GOT. For every GOT, add local, global and TLS entries. |
| void |
| lay_out_multi_got(Layout* layout, const Input_objects* input_objects); |
| |
| // Attempt to merge GOTs of different input objects. |
| void |
| merge_gots(const Input_objects* input_objects); |
| |
| // Consider merging FROM, which is OBJECT's GOT, into TO. Return false if |
| // this would lead to overflow, true if they were merged successfully. |
| bool |
| merge_got_with(Mips_got_info<size, big_endian>* from, |
| Mips_relobj<size, big_endian>* object, |
| Mips_got_info<size, big_endian>* to); |
| |
| // Return the offset of GOT page entry for VALUE. For multi-GOT links, |
| // use OBJECT's GOT. |
| unsigned int |
| get_got_page_offset(Mips_address value, |
| const Mips_relobj<size, big_endian>* object) |
| { |
| Mips_got_info<size, big_endian>* g = (!this->multi_got() |
| ? this->master_got_info_ |
| : object->get_got_info()); |
| gold_assert(g != NULL); |
| return g->get_got_page_offset(value, this); |
| } |
| |
| // Return the GOT offset of type GOT_TYPE of the global symbol |
| // GSYM. For multi-GOT links, use OBJECT's GOT. |
| unsigned int got_offset(const Symbol* gsym, unsigned int got_type, |
| Mips_relobj<size, big_endian>* object) const |
| { |
| if (!this->multi_got()) |
| return gsym->got_offset(got_type); |
| else |
| { |
| Mips_got_info<size, big_endian>* g = object->get_got_info(); |
| gold_assert(g != NULL); |
| return gsym->got_offset(g->multigot_got_type(got_type)); |
| } |
| } |
| |
| // Return the GOT offset of type GOT_TYPE of the local symbol |
| // SYMNDX. |
| unsigned int |
| got_offset(unsigned int symndx, unsigned int got_type, |
| Sized_relobj_file<size, big_endian>* object, |
| uint64_t addend) const |
| { return object->local_got_offset(symndx, got_type, addend); } |
| |
| // Return the offset of TLS LDM entry. For multi-GOT links, use OBJECT's GOT. |
| unsigned int |
| tls_ldm_offset(Mips_relobj<size, big_endian>* object) const |
| { |
| Mips_got_info<size, big_endian>* g = (!this->multi_got() |
| ? this->master_got_info_ |
| : object->get_got_info()); |
| gold_assert(g != NULL); |
| return g->tls_ldm_offset(); |
| } |
| |
| // Set the offset of TLS LDM entry. For multi-GOT links, use OBJECT's GOT. |
| void |
| set_tls_ldm_offset(unsigned int tls_ldm_offset, |
| Mips_relobj<size, big_endian>* object) |
| { |
| Mips_got_info<size, big_endian>* g = (!this->multi_got() |
| ? this->master_got_info_ |
| : object->get_got_info()); |
| gold_assert(g != NULL); |
| g->set_tls_ldm_offset(tls_ldm_offset); |
| } |
| |
| // Return true for multi-GOT links. |
| bool |
| multi_got() const |
| { return this->primary_got_ != NULL; } |
| |
| // Return the offset of OBJECT's GOT from the start of .got section. |
| unsigned int |
| get_got_offset(const Mips_relobj<size, big_endian>* object) |
| { |
| if (!this->multi_got()) |
| return 0; |
| else |
| { |
| Mips_got_info<size, big_endian>* g = object->get_got_info(); |
| return g != NULL ? g->offset() : 0; |
| } |
| } |
| |
| // Create global GOT entries that should be in the GGA_RELOC_ONLY area. |
| void |
| add_reloc_only_entries() |
| { this->master_got_info_->add_reloc_only_entries(this); } |
| |
| // Return offset of the primary GOT's entry for global symbol. |
| unsigned int |
| get_primary_got_offset(const Mips_symbol<size>* sym) const |
| { |
| gold_assert(sym->global_got_area() != GGA_NONE); |
| return (this->get_local_gotno() + sym->dynsym_index() |
| - this->first_global_got_dynsym_index()) * size/8; |
| } |
| |
| // For the entry at offset GOT_OFFSET, return its offset from the gp. |
| // Input argument GOT_OFFSET is always global offset from the start of |
| // .got section, for both single and multi-GOT links. |
| // For single GOT links, this returns GOT_OFFSET - 0x7FF0. For multi-GOT |
| // links, the return value is object_got_offset - 0x7FF0, where |
| // object_got_offset is offset in the OBJECT's GOT. |
| int |
| gp_offset(unsigned int got_offset, |
| const Mips_relobj<size, big_endian>* object) const |
| { |
| return (this->address() + got_offset |
| - this->target_->adjusted_gp_value(object)); |
| } |
| |
| protected: |
| // Write out the GOT table. |
| void |
| do_write(Output_file*); |
| |
| private: |
| |
| // This class represent dynamic relocations that need to be applied by |
| // gold because we are using TLS relocations in a static link. |
| class Static_reloc |
| { |
| public: |
| Static_reloc(unsigned int got_offset, unsigned int r_type, |
| Mips_symbol<size>* gsym) |
| : got_offset_(got_offset), r_type_(r_type), symbol_is_global_(true) |
| { this->u_.global.symbol = gsym; } |
| |
| Static_reloc(unsigned int got_offset, unsigned int r_type, |
| Sized_relobj_file<size, big_endian>* relobj, unsigned int index) |
| : got_offset_(got_offset), r_type_(r_type), symbol_is_global_(false) |
| { |
| this->u_.local.relobj = relobj; |
| this->u_.local.index = index; |
| } |
| |
| // Return the GOT offset. |
| unsigned int |
| got_offset() const |
| { return this->got_offset_; } |
| |
| // Relocation type. |
| unsigned int |
| r_type() const |
| { return this->r_type_; } |
| |
| // Whether the symbol is global or not. |
| bool |
| symbol_is_global() const |
| { return this->symbol_is_global_; } |
| |
| // For a relocation against a global symbol, the global symbol. |
| Mips_symbol<size>* |
| symbol() const |
| { |
| gold_assert(this->symbol_is_global_); |
| return this->u_.global.symbol; |
| } |
| |
| // For a relocation against a local symbol, the defining object. |
| Sized_relobj_file<size, big_endian>* |
| relobj() const |
| { |
| gold_assert(!this->symbol_is_global_); |
| return this->u_.local.relobj; |
| } |
| |
| // For a relocation against a local symbol, the local symbol index. |
| unsigned int |
| index() const |
| { |
| gold_assert(!this->symbol_is_global_); |
| return this->u_.local.index; |
| } |
| |
| private: |
| // GOT offset of the entry to which this relocation is applied. |
| unsigned int got_offset_; |
| // Type of relocation. |
| unsigned int r_type_; |
| // Whether this relocation is against a global symbol. |
| bool symbol_is_global_; |
| // A global or local symbol. |
| union |
| { |
| struct |
| { |
| // For a global symbol, the symbol itself. |
| Mips_symbol<size>* symbol; |
| } global; |
| struct |
| { |
| // For a local symbol, the object defining object. |
| Sized_relobj_file<size, big_endian>* relobj; |
| // For a local symbol, the symbol index. |
| unsigned int index; |
| } local; |
| } u_; |
| }; |
| |
| // The target. |
| Target_mips<size, big_endian>* target_; |
| // The symbol table. |
| Symbol_table* symbol_table_; |
| // The layout. |
| Layout* layout_; |
| // Static relocs to be applied to the GOT. |
| std::vector<Static_reloc> static_relocs_; |
| // .got section view. |
| unsigned char* got_view_; |
| // The dynamic symbol table index of the first symbol with global GOT entry. |
| unsigned int first_global_got_dynsym_index_; |
| // The master GOT information. |
| Mips_got_info<size, big_endian>* master_got_info_; |
| // The primary GOT information. |
| Mips_got_info<size, big_endian>* primary_got_; |
| // Secondary GOT fixups. |
| std::vector<Static_reloc> secondary_got_relocs_; |
| }; |
| |
| // A class to handle LA25 stubs - non-PIC interface to a PIC function. There are |
| // two ways of creating these interfaces. The first is to add: |
| // |
| // lui $25,%hi(func) |
| // j func |
| // addiu $25,$25,%lo(func) |
| // |
| // to a separate trampoline section. The second is to add: |
| // |
| // lui $25,%hi(func) |
| // addiu $25,$25,%lo(func) |
| // |
| // immediately before a PIC function "func", but only if a function is at the |
| // beginning of the section, and the section is not too heavily aligned (i.e we |
| // would need to add no more than 2 nops before the stub.) |
| // |
| // We only create stubs of the first type. |
| |
| template<int size, bool big_endian> |
| class Mips_output_data_la25_stub : public Output_section_data |
| { |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Mips_address; |
| |
| public: |
| Mips_output_data_la25_stub() |
| : Output_section_data(size == 32 ? 4 : 8), symbols_() |
| { } |
| |
| // Create LA25 stub for a symbol. |
| void |
| create_la25_stub(Symbol_table* symtab, Target_mips<size, big_endian>* target, |
| Mips_symbol<size>* gsym); |
| |
| // Return output address of a stub. |
| Mips_address |
| stub_address(const Mips_symbol<size>* sym) const |
| { |
| gold_assert(sym->has_la25_stub()); |
| return this->address() + sym->la25_stub_offset(); |
| } |
| |
| protected: |
| void |
| do_adjust_output_section(Output_section* os) |
| { os->set_entsize(0); } |
| |
| private: |
| // Template for standard LA25 stub. |
| static const uint32_t la25_stub_entry[]; |
| // Template for microMIPS LA25 stub. |
| static const uint32_t la25_stub_micromips_entry[]; |
| |
| // Set the final size. |
| void |
| set_final_data_size() |
| { this->set_data_size(this->symbols_.size() * 16); } |
| |
| // Create a symbol for SYM stub's value and size, to help make the |
| // disassembly easier to read. |
| void |
| create_stub_symbol(Mips_symbol<size>* sym, Symbol_table* symtab, |
| Target_mips<size, big_endian>* target, uint64_t symsize); |
| |
| // Write to a map file. |
| void |
| do_print_to_mapfile(Mapfile* mapfile) const |
| { mapfile->print_output_data(this, _(".LA25.stubs")); } |
| |
| // Write out the LA25 stub section. |
| void |
| do_write(Output_file*); |
| |
| // Symbols that have LA25 stubs. |
| std::vector<Mips_symbol<size>*> symbols_; |
| }; |
| |
| // MIPS-specific relocation writer. |
| |
| template<int sh_type, bool dynamic, int size, bool big_endian> |
| struct Mips_output_reloc_writer; |
| |
| template<int sh_type, bool dynamic, bool big_endian> |
| struct Mips_output_reloc_writer<sh_type, dynamic, 32, big_endian> |
| { |
| typedef Output_reloc<sh_type, dynamic, 32, big_endian> Output_reloc_type; |
| typedef std::vector<Output_reloc_type> Relocs; |
| |
| static void |
| write(typename Relocs::const_iterator p, unsigned char* pov) |
| { p->write(pov); } |
| }; |
| |
| template<int sh_type, bool dynamic, bool big_endian> |
| struct Mips_output_reloc_writer<sh_type, dynamic, 64, big_endian> |
| { |
| typedef Output_reloc<sh_type, dynamic, 64, big_endian> Output_reloc_type; |
| typedef std::vector<Output_reloc_type> Relocs; |
| |
| static void |
| write(typename Relocs::const_iterator p, unsigned char* pov) |
| { |
| elfcpp::Mips64_rel_write<big_endian> orel(pov); |
| orel.put_r_offset(p->get_address()); |
| orel.put_r_sym(p->get_symbol_index()); |
| orel.put_r_ssym(RSS_UNDEF); |
| orel.put_r_type(p->type()); |
| if (p->type() == elfcpp::R_MIPS_REL32) |
| orel.put_r_type2(elfcpp::R_MIPS_64); |
| else |
| orel.put_r_type2(elfcpp::R_MIPS_NONE); |
| orel.put_r_type3(elfcpp::R_MIPS_NONE); |
| } |
| }; |
| |
| template<int sh_type, bool dynamic, int size, bool big_endian> |
| class Mips_output_data_reloc : public Output_data_reloc<sh_type, dynamic, |
| size, big_endian> |
| { |
| public: |
| Mips_output_data_reloc(bool sort_relocs) |
| : Output_data_reloc<sh_type, dynamic, size, big_endian>(sort_relocs) |
| { } |
| |
| protected: |
| // Write out the data. |
| void |
| do_write(Output_file* of) |
| { |
| typedef Mips_output_reloc_writer<sh_type, dynamic, size, |
| big_endian> Writer; |
| this->template do_write_generic<Writer>(of); |
| } |
| }; |
| |
| |
| // A class to handle the PLT data. |
| |
| template<int size, bool big_endian> |
| class Mips_output_data_plt : public Output_section_data |
| { |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Mips_address; |
| typedef Mips_output_data_reloc<elfcpp::SHT_REL, true, |
| size, big_endian> Reloc_section; |
| |
| public: |
| // Create the PLT section. The ordinary .got section is an argument, |
| // since we need to refer to the start. |
| Mips_output_data_plt(Layout* layout, Output_data_space* got_plt, |
| Target_mips<size, big_endian>* target) |
| : Output_section_data(size == 32 ? 4 : 8), got_plt_(got_plt), symbols_(), |
| plt_mips_offset_(0), plt_comp_offset_(0), plt_header_size_(0), |
| target_(target) |
| { |
| this->rel_ = new Reloc_section(false); |
| layout->add_output_section_data(".rel.plt", elfcpp::SHT_REL, |
| elfcpp::SHF_ALLOC, this->rel_, |
| ORDER_DYNAMIC_PLT_RELOCS, false); |
| } |
| |
| // Add an entry to the PLT for a symbol referenced by r_type relocation. |
| void |
| add_entry(Mips_symbol<size>* gsym, unsigned int r_type); |
| |
| // Return the .rel.plt section data. |
| Reloc_section* |
| rel_plt() const |
| { return this->rel_; } |
| |
| // Return the number of PLT entries. |
| unsigned int |
| entry_count() const |
| { return this->symbols_.size(); } |
| |
| // Return the offset of the first non-reserved PLT entry. |
| unsigned int |
| first_plt_entry_offset() const |
| { return sizeof(plt0_entry_o32); } |
| |
| // Return the size of a PLT entry. |
| unsigned int |
| plt_entry_size() const |
| { return sizeof(plt_entry); } |
| |
| // Set final PLT offsets. For each symbol, determine whether standard or |
| // compressed (MIPS16 or microMIPS) PLT entry is used. |
| void |
| set_plt_offsets(); |
| |
| // Return the offset of the first standard PLT entry. |
| unsigned int |
| first_mips_plt_offset() const |
| { return this->plt_header_size_; } |
| |
| // Return the offset of the first compressed PLT entry. |
| unsigned int |
| first_comp_plt_offset() const |
| { return this->plt_header_size_ + this->plt_mips_offset_; } |
| |
| // Return whether there are any standard PLT entries. |
| bool |
| has_standard_entries() const |
| { return this->plt_mips_offset_ > 0; } |
| |
| // Return the output address of standard PLT entry. |
| Mips_address |
| mips_entry_address(const Mips_symbol<size>* sym) const |
| { |
| gold_assert (sym->has_mips_plt_offset()); |
| return (this->address() + this->first_mips_plt_offset() |
| + sym->mips_plt_offset()); |
| } |
| |
| // Return the output address of compressed (MIPS16 or microMIPS) PLT entry. |
| Mips_address |
| comp_entry_address(const Mips_symbol<size>* sym) const |
| { |
| gold_assert (sym->has_comp_plt_offset()); |
| return (this->address() + this->first_comp_plt_offset() |
| + sym->comp_plt_offset()); |
| } |
| |
| protected: |
| void |
| do_adjust_output_section(Output_section* os) |
| { os->set_entsize(0); } |
| |
| // Write to a map file. |
| void |
| do_print_to_mapfile(Mapfile* mapfile) const |
| { mapfile->print_output_data(this, _(".plt")); } |
| |
| private: |
| // Template for the first PLT entry. |
| static const uint32_t plt0_entry_o32[]; |
| static const uint32_t plt0_entry_n32[]; |
| static const uint32_t plt0_entry_n64[]; |
| static const uint32_t plt0_entry_micromips_o32[]; |
| static const uint32_t plt0_entry_micromips32_o32[]; |
| |
| // Template for subsequent PLT entries. |
| static const uint32_t plt_entry[]; |
| static const uint32_t plt_entry_r6[]; |
| static const uint32_t plt_entry_mips16_o32[]; |
| static const uint32_t plt_entry_micromips_o32[]; |
| static const uint32_t plt_entry_micromips32_o32[]; |
| |
| // Set the final size. |
| void |
| set_final_data_size() |
| { |
| this->set_data_size(this->plt_header_size_ + this->plt_mips_offset_ |
| + this->plt_comp_offset_); |
| } |
| |
| // Write out the PLT data. |
| void |
| do_write(Output_file*); |
| |
| // Return whether the plt header contains microMIPS code. For the sake of |
| // cache alignment always use a standard header whenever any standard entries |
| // are present even if microMIPS entries are present as well. This also lets |
| // the microMIPS header rely on the value of $v0 only set by microMIPS |
| // entries, for a small size reduction. |
| bool |
| is_plt_header_compressed() const |
| { |
| gold_assert(this->plt_mips_offset_ + this->plt_comp_offset_ != 0); |
| return this->target_->is_output_micromips() && this->plt_mips_offset_ == 0; |
| } |
| |
| // Return the size of the PLT header. |
| unsigned int |
| get_plt_header_size() const |
| { |
| if (this->target_->is_output_n64()) |
| return 4 * sizeof(plt0_entry_n64) / sizeof(plt0_entry_n64[0]); |
| else if (this->target_->is_output_n32()) |
| return 4 * sizeof(plt0_entry_n32) / sizeof(plt0_entry_n32[0]); |
| else if (!this->is_plt_header_compressed()) |
| return 4 * sizeof(plt0_entry_o32) / sizeof(plt0_entry_o32[0]); |
| else if (this->target_->use_32bit_micromips_instructions()) |
| return (2 * sizeof(plt0_entry_micromips32_o32) |
| / sizeof(plt0_entry_micromips32_o32[0])); |
| else |
| return (2 * sizeof(plt0_entry_micromips_o32) |
| / sizeof(plt0_entry_micromips_o32[0])); |
| } |
| |
| // Return the PLT header entry. |
| const uint32_t* |
| get_plt_header_entry() const |
| { |
| if (this->target_->is_output_n64()) |
| return plt0_entry_n64; |
| else if (this->target_->is_output_n32()) |
| return plt0_entry_n32; |
| else if (!this->is_plt_header_compressed()) |
| return plt0_entry_o32; |
| else if (this->target_->use_32bit_micromips_instructions()) |
| return plt0_entry_micromips32_o32; |
| else |
| return plt0_entry_micromips_o32; |
| } |
| |
| // Return the size of the standard PLT entry. |
| unsigned int |
| standard_plt_entry_size() const |
| { return 4 * sizeof(plt_entry) / sizeof(plt_entry[0]); } |
| |
| // Return the size of the compressed PLT entry. |
| unsigned int |
| compressed_plt_entry_size() const |
| { |
| gold_assert(!this->target_->is_output_newabi()); |
| |
| if (!this->target_->is_output_micromips()) |
| return (2 * sizeof(plt_entry_mips16_o32) |
| / sizeof(plt_entry_mips16_o32[0])); |
| else if (this->target_->use_32bit_micromips_instructions()) |
| return (2 * sizeof(plt_entry_micromips32_o32) |
| / sizeof(plt_entry_micromips32_o32[0])); |
| else |
| return (2 * sizeof(plt_entry_micromips_o32) |
| / sizeof(plt_entry_micromips_o32[0])); |
| } |
| |
| // The reloc section. |
| Reloc_section* rel_; |
| // The .got.plt section. |
| Output_data_space* got_plt_; |
| // Symbols that have PLT entry. |
| std::vector<Mips_symbol<size>*> symbols_; |
| // The offset of the next standard PLT entry to create. |
| unsigned int plt_mips_offset_; |
| // The offset of the next compressed PLT entry to create. |
| unsigned int plt_comp_offset_; |
| // The size of the PLT header in bytes. |
| unsigned int plt_header_size_; |
| // The target. |
| Target_mips<size, big_endian>* target_; |
| }; |
| |
| // A class to handle the .MIPS.stubs data. |
| |
| template<int size, bool big_endian> |
| class Mips_output_data_mips_stubs : public Output_section_data |
| { |
| typedef typename elfcpp::Elf_types<size>::Elf_Addr Mips_address; |
| |
| // Unordered set of .MIPS.stubs entries. |
| typedef Unordered_set<Mips_symbol<size>*, Mips_symbol_hash<size> > |
| Mips_stubs_entry_set; |
| |
| public: |
| Mips_output_data_mips_stubs(Target_mips<size, big_endian>* target) |
| : Output_section_data(size == 32 ? 4 : 8), symbols_(), dynsym_count_(-1U), |
| stub_offsets_are_set_(false), target_(target) |
| { } |
| |
| // Create entry for a symbol. |
| void |
| make_entry(Mips_symbol<size>*); |
| |
| // Remove entry for a symbol. |
| void |
| remove_entry(Mips_symbol<size>* gsym); |
| |
| // Set stub offsets for symbols. This method expects that the number of |
| // entries in dynamic symbol table is set. |
| void |
| set_lazy_stub_offsets(); |
| |
| void |
| set_needs_dynsym_value(); |
| |
| // Set the number of entries in dynamic symbol table. |
| void |
| set_dynsym_count(unsigned int dynsym_count) |
| { this->dynsym_count_ = dynsym_count; } |
| |
| // Return maximum size of the stub, ie. the stub size if the dynamic symbol |
| // count is greater than 0x10000. If the dynamic symbol count is less than |
| // 0x10000, the stub will be 4 bytes smaller. |
| // There's no disadvantage from using microMIPS code here, so for the sake of |
| // pure-microMIPS binaries we prefer it whenever there's any microMIPS code in |
| // output produced at all. This has a benefit of stubs being shorter by |
| // 4 bytes each too, unless in the insn32 mode. |
| unsigned int |
| stub_max_size() const |
| { |
| if (!this->target_->is_output_micromips() |
| || this->target_->use_32bit_micromips_instructions()) |
| return 20; |
| else |
| return 16; |
| } |
| |
| // Return the size of the stub. This method expects that the final dynsym |
| // count is set. |
| unsigned int |
| stub_size() const |
| { |
| gold_assert(this->dynsym_count_ != -1U); |
| if (this->dynsym_count_ > 0x10000) |
| return this->stub_max_size(); |
| else |
| return this->stub_max_size() - 4; |
| } |
| |
| // Return output address of a stub. |
| Mips_address |
| stub_address(const Mips_symbol<size>* sym) const |
| { |
| gold_assert(sym->has_lazy_stub()); |
| return this->address() + sym->lazy_stub_offset(); |
| } |
| |
| protected: |
| void |
| do_adjust_output_section(Output_section* os) |
| { os->set_entsize(0); } |
| |
| // Write to a map file. |
| void |
| do_print_to_mapfile(Mapfile* mapfile) const |
| { mapfile->print_output_data(this, _(".MIPS.stubs")); } |
| |
| private: |
| static const uint32_t lazy_stub_normal_1[]; |
| static const uint32_t lazy_stub_normal_1_n64[]; |
| static const uint32_t lazy_stub_normal_2[]; |
| static const uint32_t lazy_stub_normal_2_n64[]; |
| static const uint32_t lazy_stub_big[]; |
| static const uint32_t lazy_stub_big_n64[]; |
| |
| static const uint32_t lazy_stub_micromips_normal_1[]; |
| static const uint32_t lazy_stub_micromips_normal_1_n64[]; |
| static const uint32_t lazy_stub_micromips_normal_2[]; |
| static const uint32_t lazy_stub_micromips_normal_2_n64[]; |
| static const uint32_t lazy_stub_micromips_big[]; |
| static const uint32_t lazy_stub_micromips_big_n64[]; |
| |
| static const uint32_t lazy_stub_micromips32_normal_1[]; |
| static const uint32_t lazy_stub_micromips32_normal_1_n64[]; |
| static const uint32_t lazy_stub_micromips32_normal_2[]; |
| static const uint32_t lazy_stub_micromips32_normal_2_n64[]; |
| static const uint32_t lazy_stub_micromips32_big[]; |
| static const uint32_t lazy_stub_micromips32_big_n64[]; |
| |
| // Set the final size. |
| void |
| set_final_data_size() |
| { this->set_data_size(this->symbols_.size() * this->stub_max_size()); } |
| |
| // Write out the .MIPS.stubs data. |
| void |
| do_write(Output_file*); |
| |
| // .MIPS.stubs symbols |
| Mips_stubs_entry_set symbols_; |
| // Number of entries in dynamic symbol table. |
| unsigned int dynsym_count_; |
| // Whether the stub offsets are set. |
| bool stub_offsets_are_set_; |
| // The target. |
| Target_mips<size, big_endian>* target_; |
| }; |
| |
| // This class handles Mips .reginfo output section. |
| |
| template<int size, bool big_endian> |
| class Mips_output_section_reginfo : public Output_section_data |
| { |
| typedef typename elfcpp::Swap<size, big_endian>::Valtype Valtype; |
| |
| public: |
| Mips_output_section_reginfo(Target_mips<size, big_endian>* target, |
| Valtype gprmask, Valtype cprmask1, |
| Valtype cprmask2, Valtype cprmask3, |
| Valtype cprmask4) |
| : Output_section_data(24, 4, true), target_(target), |
| gprmask_(gprmask), cprmask1_(cprmask1), cprmask2_(cprmask2), |
| cprmask3_(cprmask3), cprmask4_(cprmask4) |
| { } |
| |
| protected: |
| // Write to a map file. |
| void |
| do_print_to_mapfile(Mapfile* mapfile) const |
| { mapfile->print_output_data(this, _(".reginfo")); } |
| |
| // Write out reginfo section. |
| void |
| do_write(Output_file* of); |
| |
| private: |
| Target_mips<size, big_endian>* target_; |
| |
| // gprmask of the output .reginfo section. |
| Valtype gprmask_; |
| // cprmask1 of the output .reginfo section. |
| Valtype cprmask1_; |
| // cprmask2 of the output .reginfo section. |
| Valtype cprmask2_; |
| // cprmask3 of the output .reginfo section. |
| Valtype cprmask3_; |
| // cprmask4 of the output .reginfo section. |
| Valtype cprmask4_; |
| }; |
| |
| // This class handles .MIPS.options output section. |
| |
| template<int size, bool big_endian> |
| class Mips_output_section_options : public Output_section |
| { |
| public: |
| Mips_output_section_options(const char* name, elfcpp::Elf_Word type, |
| elfcpp::Elf_Xword flags, |
| Target_mips<size, big_endian>* target) |
| : Output_section(name, type, flags), target_(target) |
| { |
| // After the input sections are written, we only need to update |
| // ri_gp_value field of ODK_REGINFO entries. |
| this->set_after_input_sections(); |
| } |
| |
| protected: |
| // Write out option section. |
| void |
| do_write(Output_file* of); |
| |
| private: |
| Target_mips<size, big_endian>* target_; |
| }; |
| |
| // This class handles .MIPS.abiflags output section. |
| |
| template<int size, bool big_endian> |
| class Mips_output_section_abiflags : public Output_section_data |
| { |
| public: |
| Mips_output_section_abiflags(const Mips_abiflags<big_endian>& abiflags) |
| : Output_section_data(24, 8, true), abiflags_(abiflags) |
| { } |
| |
| protected: |
| // Write to a map file. |
| void |
| do_print_to_mapfile(Mapfile* mapfile) const |
| { mapfile->print_output_data(this, _(".MIPS.abiflags")); } |
| |
| void |
| do_write(Output_file* of); |
| |
| private: |
| const Mips_abiflags<big_endian>& abiflags_; |
| }; |
| |
| // The MIPS target has relocation types which default handling of relocatable |
| // relocation cannot process. So we have to extend the default code. |
| |
| template<bool big_endian, typename Classify_reloc> |
| class Mips_scan_relocatable_relocs : |
| public Default_scan_relocatable_relocs<Classify_reloc> |
| { |
| public: |
| // Return the strategy to use for a local symbol which is a section |
| // symbol, given the relocation type. |
| inline Relocatable_relocs::Reloc_strategy |
| local_section_strategy(unsigned int r_type, Relobj* object) |
| { |
| if (Classify_reloc::sh_type == elfcpp::SHT_RELA) |
| return Relocatable_relocs::RELOC_ADJUST_FOR_SECTION_RELA; |
| else |
| { |
| switch (r_type) |
| { |
| case elfcpp::R_MIPS_26: |
| return Relocatable_relocs::RELOC_SPECIAL; |
| |
| default: |
| return Default_scan_relocatable_relocs<Classify_reloc>:: |
| local_section_strategy(r_type, object); |
| } |
| } |
| } |
| }; |
| |
| // Mips_copy_relocs class. The only difference from the base class is the |
| // method emit_mips, which should be called instead of Copy_reloc_entry::emit. |
| // Mips cannot convert all relocation types to dynamic relocs. If a reloc |
| // cannot be made dynamic, a COPY reloc is emitted. |
| |
| template<int sh_type, int size, bool big_endian> |
| class Mips_copy_relocs : public Copy_relocs<sh_type, size, big_endian> |
| { |
| public: |
| Mips_copy_relocs() |
| : Copy_relocs<sh_type, size, big_endian>(elfcpp::R_MIPS_COPY) |
| { } |
| |
| // Emit any saved relocations which turn out to be needed. This is |
| // called after all the relocs have been scanned. |
| void |
| emit_mips(Output_data_reloc<sh_type, true, size, big_endian>*, |
| Symbol_table*, Layout*, Target_mips<size, big_endian>*); |
| |
| private: |
| typedef typename Copy_relocs<sh_type, size, big_endian>::Copy_reloc_entry |
| Copy_reloc_entry; |
| |
| // Emit this reloc if appropriate. This is called after we have |
| // scanned all the relocations, so we know whether we emitted a |
| // COPY relocation for SYM_. |
| void |
| emit_entry(Copy_reloc_entry& entry, |
| Output_data_reloc<sh_type, true, size, big_endian>* reloc_section, |
| Symbol_table* symtab, Layout* layout, |
| Target_mips<size, big_endian>* target); |
| }; |
| |
| |
| // Return true if the symbol SYM should be considered to resolve local |
| // to the current module, and false otherwise. The logic is taken from |
| // GNU ld's method _bfd_elf_symbol_refs_local_p. |
| static bool |
| symbol_refs_local(const Symbol* sym, bool has_dynsym_entry, |
| bool local_protected) |
| { |
| // If it's a local sym, of course we resolve locally. |
| if (sym == NULL) |
| return true; |
| |
| // STV_HIDDEN or STV_INTERNAL ones must be local. |
| if (sym->visibility() == elfcpp::STV_HIDDEN |
| || sym->visibility() == elfcpp::STV_INTERNAL) |
| return true; |
| |
| // If we don't have a definition in a regular file, then we can't |
| // resolve locally. The sym is either undefined or dynamic. |
| if (sym->is_from_dynobj() || sym->is_undefined()) |
| return false; |
| |
| // Forced local symbols resolve locally. |
| if (sym->is_forced_local()) |
| return true; |
| |
| // As do non-dynamic symbols. |
| if (!has_dynsym_entry) |
| return true; |
| |
| // At this point, we know the symbol is defined and dynamic. In an |
| // executable it must resolve locally, likewise when building symbolic |
| // shared libraries. |
| if (parameters->options().output_is_executable() |
| || parameters->options().Bsymbolic()) |
| return true; |
| |
| // Now deal with defined dynamic symbols in shared libraries. Ones |
| // with default visibility might not resolve locally. |
| if (sym->visibility() == elfcpp::STV_DEFAULT) |
| return false; |
| |
| // STV_PROTECTED non-function symbols are local. |
| if (sym->type() != elfcpp::STT_FUNC) |
| return true; |
| |
| // Function pointer equality tests may require that STV_PROTECTED |
| // symbols be treated as dynamic symbols. If the address of a |
| // function not defined in an executable is set to that function's |
| // plt entry in the executable, then the address of the function in |
| // a shared library must also be the plt entry in the executable. |
| return local_protected; |
| } |
| |
| // Return TRUE if references to this symbol always reference the symbol in this |
| // object. |
| static bool |
| symbol_references_local(const Symbol* sym, bool has_dynsym_entry) |
| { |
| return symbol_refs_local(sym, has_dynsym_entry, false); |
| } |
| |
| // Return TRUE if calls to this symbol always call the version in this object. |
| static bool |
| symbol_calls_local(const Symbol* sym, bool has_dynsym_entry) |
| { |
| return symbol_refs_local(sym, has_dynsym_entry, true); |
| } |
| |
| // Compare GOT offsets of two symbols. |
| |
| template<int size, bool big_endian> |
| static bool |
| got_offset_compare(Symbol* sym1, Symbol* sym2) |
| { |
| Mips_symbol<size>* mips_sym1 = Mips_symbol<size>::as_mips_sym(sym1); |
| Mips_symbol<size>* mips_sym2 = Mips_symbol<size>::as_mips_sym(sym2); |
| unsigned int area1 = mips_sym1->global_got_area(); |
| unsigned int area2 = mips_sym2->global_got_area(); |
| gold_assert(area1 != GGA_NONE && area1 != GGA_NONE); |
| |
| // GGA_NORMAL entries always come before GGA_RELOC_ONLY. |
| if (area1 != area2) |
| return area1 < area2; |
| |
| return mips_sym1->global_gotoffset() < mips_sym2->global_gotoffset(); |
| } |
| |
| // This method divides dynamic symbols into symbols that have GOT entry, and |
| // symbols that don't have GOT entry. It also sorts symbols with the GOT entry. |
| // Mips ABI requires that symbols with the GOT entry must be at the end of |
| // dynamic symbol table, and the order in dynamic symbol table must match the |
| // order in GOT. |
| |
| template<int size, bool big_endian> |
| static void |
| reorder_dyn_symbols(std::vector<Symbol*>* dyn_symbols, |
| std::vector<Symbol*>* non_got_symbols, |
| std::vector<Symbol*>* got_symbols) |
| { |
| for (std::vector<Symbol*>::iterator p = dyn_symbols->begin(); |
| p != dyn_symbols->end(); |
| ++p) |
| { |
| Mips_symbol<size>* mips_sym = Mips_symbol<size>::as_mips_sym(*p); |
| if (mips_sym->global_got_area() == GGA_NORMAL |
| || mips_sym->global_got_area() == GGA_RELOC_ONLY) |
| got_symbols->push_back(mips_sym); |
| else |
| non_got_symbols->push_back(mips_sym); |
| } |
| |
| std::sort(got_symbols->begin(), got_symbols->end(), |
| got_offset_compare<size, big_endian>); |
| } |
| |
| // Functor class for processing the global symbol table. |
| |
| template<int size, bool big_endian> |
| class Symbol_visitor_check_symbols |
| { |
| public: |
| Symbol_visitor_check_symbols(Target_mips<size, big_endian>* target, |
| Layout* layout, Symbol_table* symtab) |
| : target_(target), layout_(layout), symtab_(symtab) |
| { } |
| |
| void |
| operator()(Sized_symbol<size>* sym) |
| { |
| Mips_symbol<size>* mips_sym = Mips_symbol<size>::as_mips_sym(sym); |
| if (local_pic_function<size, big_endian>(mips_sym)) |
| { |
| // SYM is a function that might need $25 to be valid on entry. |
| // If we're creating a non-PIC relocatable object, mark SYM as |
| // being PIC. If we're creating a non-relocatable object with |
| // non-PIC branches and jumps to SYM, make sure that SYM has an la25 |
| // stub. |
| if (parameters->options().relocatable()) |
| { |
| if (!parameters->options().output_is_position_independent()) |
| mips_sym->set_pic(); |
| } |
| else if (mips_sym->has_nonpic_branches()) |
| { |
| this->target_->la25_stub_section(layout_) |
| ->create_la25_stub(this->symtab_, this->target_, mips_sym); |
| } |
| } |
| } |
| |
| private: |
| Target_mips<size, big_endian>* target_; |
| Layout* layout_; |
| Symbol_table* symtab_; |
| }; |
| |
| // Relocation types, parameterized by SHT_REL vs. SHT_RELA, size, |
| // and endianness. The relocation format for MIPS-64 is non-standard. |
| |
| template<int sh_type, int size, bool big_endian> |
| struct Mips_reloc_types; |
| |
| template<bool big_endian> |
| struct Mips_reloc_types<elfcpp::SHT_REL, 32, big_endian> |
| { |
| typedef typename elfcpp::Rel<32, big_endian> Reloc; |
| typedef typename elfcpp::Rel_write<32, big_endian> Reloc_write; |
| |
| static typename elfcpp::Elf_types<32>::Elf_Swxword |
| get_r_addend(const Reloc*) |
| { return 0; } |
| |
| static inline void |
| set_reloc_addend(Reloc_write*, |
| typename elfcpp::Elf_types<32>::Elf_Swxword) |
| { gold_unreachable(); } |
| }; |
| |
| template<bool big_endian> |
| struct Mips_reloc_types<elfcpp::SHT_RELA, 32, big_endian> |
| { |
| typedef typename elfcpp::Rela<32, big_endian> Reloc; |
| typedef typename elfcpp::Rela_write<32, big_endian> Reloc_write; |
| |
| static typename elfcpp::Elf_types<32>::Elf_Swxword |
| get_r_addend(const Reloc* reloc) |
| { return reloc->get_r_addend(); } |
| |
| static inline void |
| set_reloc_addend(Reloc_write* p, |
| typename elfcpp::Elf_types<32>::Elf_Swxword val) |
| { p->put_r_addend(val); } |
| }; |
| |
| template<bool big_endian> |
| struct Mips_reloc_types<elfcpp::SHT_REL, 64, big_endian> |
| { |
| typedef typename elfcpp::Mips64_rel<big_endian> Reloc; |
| typedef typename elfcpp::Mips64_rel_write<big_endian> Reloc_write; |
| |
| static typename elfcpp::Elf_types<64>::Elf_Swxword |
| get_r_addend(const Reloc*) |
| { return 0; } |
| |
| static <
|