blob: 8893e315dd57d78b1c36424bce4d2cffbb8a59ec [file] [log] [blame]
// mips.cc -- mips target support for gold.
// Copyright (C) 2011-2016 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_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;
}
// Return whether this entry is equal to OTHER.
bool
equals(Mips_got_entry<size, big_endian>* other) const
{
if (this->tls_type_ == GOT_TLS_LDM)
return true;
return ((this->tls_type_ == other->tls_type_)
&& (this->symndx_ == other->symndx_)
&& ((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), num_pages(0)
{ }
Got_page_entry(Object* object_, unsigned int symndx_)
: object(object_), symndx(symndx_), ranges(NULL), num_pages(0)
{ }
// 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;
// The maximum number of page entries needed for RANGES.
unsigned int num_pages;
};
// 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_entries(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.
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),
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 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;
// 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.
const 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.abiflags output section.
template<int size, bool big_endian>
class Mips_output_section_abiflags : public Output_section_data
{
public: