This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Mips target in gold


Resending the message in plain text format.



From: Sasa Stankovic
Sent: Wednesday, July 03, 2013 12:46 PM
To: binutils@sourceware.org
Cc: iant@google.com; Petar Jovanovic
Subject: Mips target in gold



Hi all,

Please find attached first of the patches that implement Mips target in gold. This patch contains first part of the Mips specific changes (mips.cc file is split in two patches). Mips32, mips16 and micromips architectures are currently supported.

Regards,
Sasa

diff --git a/elfcpp/mips.h b/elfcpp/mips.h
index 8c2d8f4..a5640eb 100644
--- a/elfcpp/mips.h
+++ b/elfcpp/mips.h
@@ -1,7 +1,8 @@
 // mips.h -- ELF definitions specific to EM_MIPS  -*- C++ -*-
 
 // Copyright 2012 Free Software Foundation, Inc.
-// Written by Aleksandar Simeonov <aleksandar.simeonov@rt-rk.com>.
+// Written by Sasa Stankovic <sasa.stankovic@rt-rk.com>
+//        and Aleksandar Simeonov <aleksandar.simeonov@rt-rk.com>.
 
 // This file is part of elfcpp.
 
@@ -96,21 +97,67 @@ enum
   R_MIPS_TLS_TPREL_HI16 = 49,
   R_MIPS_TLS_TPREL_LO16 = 50,
   R_MIPS_GLOB_DAT = 51,
+  // These relocs are used for the mips16.
   R_MIPS16_26 = 100,
   R_MIPS16_GPREL = 101,
   R_MIPS16_GOT16 = 102,
   R_MIPS16_CALL16 = 103,
   R_MIPS16_HI16 = 104,
   R_MIPS16_LO16 = 105,
+  R_MIPS16_TLS_GD = 106,
+  R_MIPS16_TLS_LDM = 107,
+  R_MIPS16_TLS_DTPREL_HI16 = 108,
+  R_MIPS16_TLS_DTPREL_LO16 = 109,
+  R_MIPS16_TLS_GOTTPREL = 110,
+  R_MIPS16_TLS_TPREL_HI16 = 111,
+  R_MIPS16_TLS_TPREL_LO16 = 112,
+
   R_MIPS_COPY = 126,
   R_MIPS_JUMP_SLOT = 127,
+
+  // These relocations are specific to microMIPS.
+  R_MICROMIPS_26_S1 = 133,
+  R_MICROMIPS_HI16 = 134,
+  R_MICROMIPS_LO16 = 135,
+  R_MICROMIPS_GPREL16 = 136,       // In Elf 64: alias R_MICROMIPS_GPREL
+  R_MICROMIPS_LITERAL = 137,
+  R_MICROMIPS_GOT16 = 138,         // In Elf 64: alias R_MICROMIPS_GOT
+  R_MICROMIPS_PC7_S1 = 139,
+  R_MICROMIPS_PC10_S1 = 140,
+  R_MICROMIPS_PC16_S1 = 141,
+  R_MICROMIPS_CALL16 = 142,        // In Elf 64: alias R_MICROMIPS_CALL
+  R_MICROMIPS_GOT_DISP = 145,
+  R_MICROMIPS_GOT_PAGE = 146,
+  R_MICROMIPS_GOT_OFST = 147,
+  R_MICROMIPS_GOT_HI16 = 148,
+  R_MICROMIPS_GOT_LO16 = 149,
+  R_MICROMIPS_SUB = 150,
+  R_MICROMIPS_HIGHER = 151,
+  R_MICROMIPS_HIGHEST = 152,
+  R_MICROMIPS_CALL_HI16 = 153,
+  R_MICROMIPS_CALL_LO16 = 154,
+  R_MICROMIPS_SCN_DISP = 155,
+  R_MICROMIPS_JALR = 156,
+  R_MICROMIPS_HI0_LO16 = 157,
+  // TLS relocations.
+  R_MICROMIPS_TLS_GD = 162,
+  R_MICROMIPS_TLS_LDM = 163,
+  R_MICROMIPS_TLS_DTPREL_HI16 = 164,
+  R_MICROMIPS_TLS_DTPREL_LO16 = 165,
+  R_MICROMIPS_TLS_GOTTPREL = 166,
+  R_MICROMIPS_TLS_TPREL_HI16 = 169,
+  R_MICROMIPS_TLS_TPREL_LO16 = 170,
+  // microMIPS GP- and PC-relative relocations.
+  R_MICROMIPS_GPREL7_S2 = 172,
+  R_MICROMIPS_PC23_S2 = 173,
+
   R_MIPS_PC32 = 248,
   R_MIPS_GNU_REL16_S2 = 250,
   R_MIPS_GNU_VTINHERIT = 253,
   R_MIPS_GNU_VTENTRY = 254
 };
 
-// Processor specific flags for the ELF header e_flags field.  */
+// Processor specific flags for the ELF header e_flags field.
 enum
 {
   // At least one .noreorder directive appears in the source.
@@ -262,6 +309,26 @@ enum
   DTP_OFFSET = 0x8000
 };
 
+
+bool
+elf_st_is_mips16(unsigned char st_other)
+{ return (st_other & elfcpp::STO_MIPS16) == elfcpp::STO_MIPS16; }
+
+bool
+elf_st_is_micromips(unsigned char st_other)
+{ return (st_other & elfcpp::STO_MIPS_ISA) == elfcpp::STO_MICROMIPS; }
+
+// Whether the ABI is N32.
+bool
+abi_n32(elfcpp::Elf_Word e_flags)
+{ return (e_flags & elfcpp::EF_MIPS_ABI2) != 0; }
+
+// Whether the ABI is N64.
+bool
+abi_64(unsigned char ei_class)
+{ return ei_class == elfcpp::ELFCLASS64; }
+
+
 } // End namespace elfcpp.
 
 #endif // !defined(ELFCPP_MIPS_H)
diff --git a/gold/mips.cc b/gold/mips.cc
new file mode 100644
index 0000000..a380560
--- /dev/null
+++ b/gold/mips.cc
@@ -0,0 +1,4442 @@
+// mips.cc -- mips target support for gold.
+
+// Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
+// Written by Sasa Stankovic <sasa.stankovic@rt-rk.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 "target.h"
+#include "target-reloc.h"
+#include "target-select.h"
+#include "tls.h"
+#include "errors.h"
+#include "gc.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;
+
+// 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
+};
+
+// 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
+//          (object != NULL, symndx == -1)
+//    (3) a TLS LDM slot
+//          (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(Sized_relobj_file<size, big_endian> *object,
+                 unsigned int symndx, Mips_address addend,
+                 unsigned char tls_type, unsigned int shndx)
+    : object_(object), symndx_(symndx), tls_type_(tls_type), shndx_(shndx)
+  { this->d.addend = addend; }
+
+  Mips_got_entry(Sized_relobj_file<size, big_endian> *object,
+                 Mips_symbol<size> *sym, unsigned char tls_type)
+    : object_(object), symndx_(-1U), tls_type_(tls_type), shndx_(-1U)
+  { this->d.sym = sym; }
+
+  bool
+  is_for_local_symbol() const
+  { return this->object_ != NULL && this->symndx_ != -1U; }
+
+  bool
+  is_for_global_symbol() const
+  { return this->object_ != NULL && this->symndx_ == -1U; }
+
+  size_t
+  hash() const
+  {
+    uintptr_t object_id = reinterpret_cast<uintptr_t>(this->object_);
+    uintptr_t sym_id = reinterpret_cast<uintptr_t>(this->d.sym);
+    return (this->symndx_
+            + ((this->tls_type_ == GOT_TLS_LDM) << 18)
+            + (this->tls_type_ == GOT_TLS_LDM
+               ? 0
+               : this->symndx_ != -1U
+                   ? (object_id + this->d.addend)
+                   : sym_id));
+  }
+
+  bool
+  equals(Mips_got_entry<size, big_endian> *other) const
+  {
+    return (this->symndx_ == other->symndx_
+            && this->tls_type_ == other->tls_type_
+            && (this->tls_type_ == GOT_TLS_LDM
+                ? true
+                : this->symndx_ != -1U
+                    ? (this->object_ == other->object_
+                       && this->d.addend == other->d.addend)
+                    : this->d.sym == other->d.sym));
+  }
+
+  // The input object that needs the GOT entry.
+  Sized_relobj_file<size, big_endian>*
+  object() const
+  { return this->object_; }
+
+  // The index of the symbol if we have a local symbol; -1 otherwise.
+  unsigned int
+  symndx() const
+  { return this->symndx_; }
+
+  Mips_address
+  addend() const
+  {
+    gold_assert(this->symndx_ != -1U);
+    return this->d.addend;
+  }
+
+  Mips_symbol<size>*
+  sym() const
+  {
+    gold_assert(this->symndx_ == -1U);
+    return this->d.sym;
+  }
+
+  bool
+  tls_entry() const
+  { return this->tls_type_ != GOT_TLS_NONE; }
+
+  unsigned char
+  tls_type() const
+  { return this->tls_type_; }
+
+  unsigned int
+  shndx() const
+  { return this->shndx_; }
+
+ private:
+  // The input object that needs the GOT entry.
+  Sized_relobj_file<size, big_endian>* object_;
+  // The index of the symbol if we have a local symbol; -1 otherwise.
+  unsigned int symndx_;
+
+  union
+  {
+    // If object != NULL && symndx != -1, the addend of the relocation
+    // that should be added to the symbol value.
+    Mips_address addend;
+    // If object != NULL && 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_;
+
+  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); }
+};
+
+// 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
+  {
+    uintptr_t object_id1 = reinterpret_cast<uintptr_t>(entry1->object);
+    uintptr_t object_id2 = reinterpret_cast<uintptr_t>(entry2->object);
+    return object_id1 == object_id2 && 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 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;
+
+ public:
+  Mips_got_info()
+    : global_gotno_(0), reloc_only_gotno_(0), local_gotno_(0),
+      page_gotno_(0), tls_gotno_(0), tls_ldm_offset_(-1U),
+      got_entries_(256, Mips_got_entry_hash<size, big_endian>(),
+                        Mips_got_entry_eq<size, big_endian>()),
+      got_page_offset_start_(0), got_page_offset_next_(0),
+      next_(NULL), index_(-1U), offset_(0)
+  { }
+
+  // Reserve space for a GOT entry containing the value of symbol
+  // SYMNDX in input object OBJECT, plus ADDEND.
+  void
+  record_local_got_symbol(Sized_relobj_file<size, big_endian>* object,
+                          unsigned int symndx,
+                          typename elfcpp::Elf_types<size>::Elf_Addr addend,
+                          unsigned int r_type,
+                          unsigned int shndx);
+  void
+  record_global_got_symbol(Mips_symbol<size>* mips_sym,
+                           Sized_relobj_file<size, big_endian>* object,
+                           unsigned int r_type, bool reloc, bool for_call);
+  void
+  record_got_entry(Sized_relobj_file<size, big_endian> *object,
+                   Mips_got_entry<size, big_endian> *entry);
+  Mips_got_info<size, big_endian>*
+  get_got_info(const Sized_relobj_file<size, big_endian> *object, bool create);
+  void
+  add_local_entries(Target_mips<size, big_endian> *target, Layout* layout);
+  void
+  add_page_entries(Target_mips<size, big_endian> *target, Layout* layout);
+  void
+  add_global_entries(Target_mips<size, big_endian> *target, Layout* layout,
+                     unsigned int non_reloc_only_global_gotno);
+  void
+  add_reloc_only_entries(Mips_output_data_got<size, big_endian> *got);
+  void
+  add_tls_entries(Target_mips<size, big_endian> *target, Layout* layout);
+  void
+  lay_out_got(Target_mips<size, big_endian> *target, Layout* layout,
+              Symbol_table* symtab, const Input_objects* input_objects);
+  void
+  count_got_symbols(Symbol_table* symtab);
+  unsigned int
+  get_got_page_offset(unsigned int value, unsigned char *got_view);
+
+  // Record that OBJECT has a page relocation against symbol SYMNDX and
+  // that ADDEND is the addend for that relocation.
+  void
+  record_got_page_entry(Sized_relobj_file<size, big_endian> *object,
+                        unsigned int symndx, int addend);
+
+  void
+  lay_out_multi_got(Target_mips<size, big_endian> *target, Layout* layout,
+                    const Input_objects* input_objects);
+
+  void
+  count_got_entries(Mips_got_info<size, big_endian> *g);
+
+  // Add the number of GOT entries required by ENTRY to G.
+  void
+  count_got_entry(Mips_got_entry<size, big_endian> *entry,
+                  Mips_got_info<size, big_endian> *g);
+
+  void
+  add_got_entries(Mips_got_info<size, big_endian> *from,
+                  Mips_got_info<size, big_endian> *to);
+
+  void
+  add_got_page_entries(Mips_got_info<size, big_endian> *from,
+                       Mips_got_info<size, big_endian> *to);
+
+  // Record that OBJECT should use output GOT G.
+  void
+  replace_object_got(Mips_relobj<size, big_endian>* mips_relobj,
+                     Mips_got_info<size, big_endian> *g);
+
+  // 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.  Returns false if
+  // this would lead to overflow, true if they were merged successfully.
+  bool
+  merge_got_with(Mips_relobj<size, big_endian>* mips_relobj,
+                 Mips_got_info<size, big_endian> *from,
+                 Mips_got_info<size, big_endian> *to,
+                 Mips_got_info<size, big_endian> *primary);
+
+  // The number of local .got entries, eventually including page entries.
+  unsigned int
+  local_gotno() const
+  { return this->local_gotno_; }
+
+  // The maximum number of page entries needed.
+  unsigned int
+  page_gotno() const
+  { return this->page_gotno_; }
+
+  Mips_got_info<size, big_endian>*
+  next() const
+  { return this->next_; }
+
+  unsigned int
+  index() const
+  { return this->index_; }
+
+  unsigned int
+  offset() const
+  { return this->offset_; }
+
+  unsigned int
+  tls_ldm_offset() const
+  { return this->tls_ldm_offset_; }
+
+  void
+  set_tls_ldm_offset(unsigned int tls_ldm_offset)
+  { this->tls_ldm_offset_ = tls_ldm_offset; }
+
+  Unordered_set<Mips_symbol<size>*>&
+  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 (Target_mips<size, big_endian>::tls_gd_reloc(r_type))
+      return GOT_TLS_GD;
+
+    if (Target_mips<size, big_endian>::tls_ldm_reloc(r_type))
+      return GOT_TLS_LDM;
+
+    if (Target_mips<size, big_endian>::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 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 local .got entries, eventually including page entries.
+  unsigned int local_gotno_;
+  // The maximum number of page entries needed.
+  unsigned int page_gotno_;
+  // The number of .got slots used for TLS.
+  unsigned int tls_gotno_;
+
+  // This is the GOT index of the TLS LDM entry for the GOT.
+  unsigned int tls_ldm_offset_;
+
+  Unordered_set<Mips_symbol<size>*> global_got_symbols_;
+
+  // A hash table holding got entries.
+  Got_entry_set got_entries_;
+
+  // A hash table of Got_page_entry structures.
+  Got_page_entry_set got_page_entries_;
+
+  unsigned int got_page_offset_start_;
+  unsigned int got_page_offset_next_;
+
+  Got_page_offsets got_page_offsets_;
+
+  // In multi-got links, a pointer to the next got.
+  Mips_got_info<size, big_endian> *next_;
+  unsigned int index_;
+  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
+{
+  got16_addend(const Sized_relobj_file<size, big_endian>* _object,
+               unsigned int _shndx, unsigned int _r_type, unsigned int _r_sym,
+               typename elfcpp::Elf_types<size>::Elf_Addr _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;
+  typename elfcpp::Elf_types<size>::Elf_Addr addend;
+};
+
+// 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), has_la25_stub_(false),
+      la25_stub_offset_(0), 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)
+  { }
+
+  bool
+  is_mips16() const
+  {
+    return ((this->nonvis() & (elfcpp::STO_MIPS16 >> 2))
+            == elfcpp::STO_MIPS16 >> 2);
+  }
+
+  bool
+  is_micromips() const
+  {
+    return ((this->nonvis() & (elfcpp::STO_MIPS_ISA >> 2))
+            == elfcpp::STO_MICROMIPS >> 2);
+  }
+
+  // Return whether we need the fn_stub; this is true if this symbol appears
+  // in any relocs other than a 16 bit call.
+  bool
+  need_fn_stub() const
+  { return this->need_fn_stub_; }
+
+  // Set whether we need the fn_stub.
+  void
+  set_need_fn_stub(bool value)
+  { this->need_fn_stub_ = value; }
+
+  bool
+  has_nonpic_branches() const
+  { return this->has_nonpic_branches_; }
+
+  void
+  set_has_nonpic_branches(bool value)
+  { this->has_nonpic_branches_ = value; }
+
+  bool
+  has_la25_stub() const
+  { return this->has_la25_stub_; }
+
+  void
+  set_has_la25_stub()
+  { this->has_la25_stub_ = true; }
+
+  unsigned int
+  la25_stub_offset() const
+  { return this->la25_stub_offset_; }
+
+  void
+  set_la25_stub_offset(unsigned int offset)
+  { this->la25_stub_offset_ = offset; }
+
+  bool
+  has_static_relocs() const
+  { return this->has_static_relocs_; }
+
+  void
+  set_has_static_relocs()
+  { this->has_static_relocs_ = true; }
+
+  bool
+  no_lazy_stub() const
+  { return this->no_lazy_stub_; }
+
+  void
+  set_no_lazy_stub()
+  { this->no_lazy_stub_ = true; }
+
+  unsigned int
+  lazy_stub_offset() const
+  { return this->lazy_stub_offset_; }
+
+  void
+  set_lazy_stub_offset(unsigned int offset)
+  { this->lazy_stub_offset_ = offset; }
+
+  bool
+  pointer_equality_needed() const
+  { return this->pointer_equality_needed_; }
+
+  void
+  set_pointer_equality_needed()
+  { this->pointer_equality_needed_ = true; }
+
+  Global_got_area
+  global_got_area() const
+  { return this->global_got_area_; }
+
+  void
+  set_global_got_area(Global_got_area global_got_area)
+  { this->global_got_area_ = global_got_area; }
+
+  unsigned int
+  global_gotoffset() const
+  { return this->global_gotoffset_; }
+
+  void
+  set_global_gotoffset(unsigned int offset)
+  {
+    if (this->global_gotoffset_ == -1U || offset < this->global_gotoffset_)
+      this->global_gotoffset_ = offset;
+  }
+
+  bool
+  got_only_for_calls() const
+  { return this->got_only_for_calls_; }
+
+  void
+  set_got_only_for_calls(bool got_only_for_calls)
+  { this->got_only_for_calls_ = got_only_for_calls; }
+
+  bool
+  is_pic() const
+  { return (this->nonvis() & 0xF) == elfcpp::STO_MIPS_PIC >> 2; }
+
+  void
+  set_pic()
+  { this->set_nonvis((this->nonvis() & ~0xF) | elfcpp::STO_MIPS_PIC >> 2); }
+
+  // Downcast a base pointer to an Mips_symbol pointer.
+  static Mips_symbol<size>*
+  as_mips_sym(Symbol *sym)
+  { return static_cast<Mips_symbol<size>*>(sym); }
+
+  static const Mips_symbol<size>*
+  as_mips_sym(const Symbol *sym)
+  { return static_cast<const Mips_symbol<size>*>(sym); }
+
+  bool
+  has_lazy_stub() const
+  { return this->has_lazy_stub_; }
+
+  void
+  set_has_lazy_stub(bool has_lazy_stub)
+  { this->has_lazy_stub_ = has_lazy_stub; }
+
+ private:
+  unsigned char
+  st_other() const
+  { return (this->nonvis() << 2) | this->visibility(); }
+
+  // Whether we need the fn_stub.
+  bool need_fn_stub_ : 1;
+
+  // 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_ : 1;
+
+  bool has_la25_stub_;
+
+  // The offset of the la25 stub for this symbol from the start of its
+  // la25 stab section.
+  unsigned int la25_stub_offset_;
+
+  // True if there is a relocation against this symbol that must be
+  // resolved by the static linker (in other words, if the relocation
+  // cannot possibly be made dynamic).
+  bool has_static_relocs_;
+
+  bool no_lazy_stub_;
+
+  unsigned int lazy_stub_offset_;
+
+  bool pointer_equality_needed_;
+
+  Global_got_area global_got_area_;
+
+  unsigned int global_gotoffset_;
+  bool got_only_for_calls_;
+
+  // Whether the symbol has lazy-binding stub.
+  bool has_lazy_stub_;
+};
+
+// This is a helper class that records relocation found in mips16 stub
+// section, and the relocation symbol. It is used to find target symbol
+// for mips16 stub.
+
+struct mips16_stub_reloc
+{
+  mips16_stub_reloc(unsigned int r_type_, unsigned int r_sym_, Symbol* gsym_)
+    : r_type(r_type_), r_sym(r_sym_), gsym(gsym_)
+  { }
+
+  // Return whether relocation refers to a local symbol.
+  bool
+  is_local() const
+  { return this->gsym == NULL; }
+
+  // Relocation type.
+  unsigned int r_type;
+  // The symbol index to which relocation refers.
+  unsigned int r_sym;
+  // The global symbol.
+  Symbol* gsym;
+};
+
+// Mips16_stub_section class.
+
+class Mips16_stub_section
+{
+ public:
+  Mips16_stub_section(Object* object, unsigned int shndx)
+    : object_(object), shndx_(shndx), r_sym_(0), gsym_(NULL),
+      is_fn_stub_(false), is_call_stub_(false), is_call_fp_stub_(false)
+  {
+    const char *section_name = object->section_name(shndx).c_str();
+    is_fn_stub_ = is_prefix_of(".mips16.fn", section_name);
+    is_call_stub_ = is_prefix_of(".mips16.call.", section_name);
+    is_call_fp_stub_ = is_prefix_of(".mips16.call.fp.", section_name);
+  }
+
+  Object*
+  object() const
+  { return this->object_; }
+
+  // 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.
+  Symbol*
+  gsym() const
+  { return this->gsym_; }
+
+  // Return whether stub is for a local function.
+  bool
+  is_for_local_function() const
+  { return this->gsym_ == NULL; }
+
+  // Find target symbol for this stub.
+  void
+  find_target_from_relocs()
+  {
+    // Trust the first R_MIPS_NONE relocation, if any.
+    std::list<mips16_stub_reloc*>::iterator it;
+    mips16_stub_reloc* reloc = NULL;
+    for (it = this->relocs_.begin(); it != this->relocs_.end(); ++it)
+      {
+        if ((*it)->r_type == elfcpp::R_MIPS_NONE)
+          {
+            reloc = *it;
+            break;
+          }
+      }
+
+    // Otherwise trust the first relocation, whatever its kind.
+    if (reloc == NULL)
+      {
+        if (this->relocs_.size() > 0)
+          reloc = *this->relocs_.begin();
+        else
+          gold_error(_("no relocation found in mips16 stub section '%s'"),
+                     this->object_->section_name(this->shndx_).c_str());
+      }
+
+    if (reloc->is_local())
+      this->r_sym_ = reloc->r_sym;
+    else
+      this->gsym_ = reloc->gsym;
+  }
+
+  void
+  add_stub_reloc(mips16_stub_reloc* stub_reloc)
+  { this->relocs_.push_back(stub_reloc); }
+
+  bool
+  is_fn_stub() const
+  { return this->is_fn_stub_; }
+
+  bool
+  is_call_stub() const
+  { return this->is_call_stub_; }
+
+  bool
+  is_call_fp_stub() const
+  { return this->is_call_fp_stub_; }
+
+  Output_section*
+  output_section()
+  { return this->object_->output_section(this->shndx_); }
+
+  uint64_t
+  output_section_offset()
+  { return this->object_->output_section_offset(this->shndx_); }
+
+ private:
+  Object* object_;
+  unsigned int shndx_;
+  unsigned int r_sym_;
+  Symbol* gsym_;
+  std::list<mips16_stub_reloc*> relocs_;
+  bool is_fn_stub_;
+  bool is_call_stub_;
+  bool is_call_fp_stub_;
+};
+
+// Mips_relobj class.
+
+template<int size, bool big_endian>
+class Mips_relobj : public Sized_relobj_file<size, big_endian>
+{
+ 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), gp_(0), got_info_(NULL)
+  {
+    this->is_pic_ = (ehdr.get_e_flags() & elfcpp::EF_MIPS_PIC) != 0;
+    this->is_n32_ = elfcpp::abi_n32(ehdr.get_e_flags());
+    this->is_n64_ = elfcpp::abi_64(ehdr.get_e_ident()[elfcpp::EI_CLASS]);
+  }
+
+  ~Mips_relobj()
+  { }
+
+  // Downcast a base pointer to an 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); }
+
+  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];
+  }
+
+  std::map<unsigned int, Mips16_stub_section*>&
+  get_mips16_stub_sections()
+  { return this->mips16_stub_sections_; }
+
+  Mips16_stub_section*
+  get_mips16_stub_section(unsigned int shndx) const
+  {
+    typename std::map<unsigned int, Mips16_stub_section*>::const_iterator it;
+    it = this->mips16_stub_sections_.find(shndx);
+    if (it != this->mips16_stub_sections_.end())
+      return (*it).second;
+    return NULL;
+  }
+
+  void
+  add_mips16_stub_section(Mips16_stub_section* stub_section)
+  {
+    this->mips16_stub_sections_.insert(
+      std::pair<unsigned int, Mips16_stub_section*>(
+        stub_section->shndx(), stub_section));
+  }
+
+  Mips16_stub_section*
+  get_local_mips16_fn_stub(unsigned int r_sym) const
+  {
+    typename std::map<unsigned int, Mips16_stub_section*>::const_iterator it;
+    it = this->local_mips16_fn_stubs_.find(r_sym);
+    if (it != this->local_mips16_fn_stubs_.end())
+      return (*it).second;
+    return NULL;
+  }
+
+  void
+  set_local_mips16_fn_stub(Mips16_stub_section* 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*>(
+        r_sym, stub));
+  }
+
+  Mips16_stub_section*
+  get_local_mips16_call_stub(unsigned int r_sym) const
+  {
+    typename std::map<unsigned int, Mips16_stub_section*>::const_iterator it;
+    it = this->local_mips16_call_stubs_.find(r_sym);
+    if (it != this->local_mips16_call_stubs_.end())
+      return (*it).second;
+    return NULL;
+  }
+
+  void
+  set_local_mips16_call_stub(Mips16_stub_section* 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*>(
+        r_sym, stub));
+  }
+
+  void
+  add_local_non_16bit_call(unsigned int r_symindx)
+  { this->local_non_16bit_calls_.insert(r_symindx); }
+
+  bool
+  has_local_non_16bit_call_relocs(unsigned int symndx)
+  {
+    return (this->local_non_16bit_calls_.find(symndx)
+            != this->local_non_16bit_calls_.end());
+  }
+
+  void
+  add_local_16bit_call(unsigned int r_symindx)
+  { this->local_16bit_calls_.insert(r_symindx); }
+
+  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.
+  typename elfcpp::Elf_types<size>::Elf_Addr
+  gp_value() const
+  { return this->gp_; }
+
+  bool
+  is_pic() const
+  { return this->is_pic_; }
+
+  bool
+  is_n32() const
+  { return this->is_n32_; }
+
+  bool
+  is_n64() const
+  { return this->is_n64_; }
+
+  // Whether the object is using NewABI conventions.
+  bool
+  is_newabi() const
+  { return this->is_n32_ || this->is_n64_; }
+
+  Mips_got_info<size, big_endian>*
+  get_got_info() const
+  { return got_info_; }
+
+  void
+  set_got_info(Mips_got_info<size, big_endian> *got_info)
+  { this->got_info_ = got_info; }
+
+ 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:
+
+  // 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_;
+
+  std::map<unsigned int, Mips16_stub_section*> mips16_stub_sections_;
+  std::set<unsigned int> local_non_16bit_calls_;
+  std::set<unsigned int> local_16bit_calls_;
+
+  std::map<unsigned int, Mips16_stub_section*> local_mips16_fn_stubs_;
+  std::map<unsigned int, Mips16_stub_section*> local_mips16_call_stubs_;
+
+  typename elfcpp::Elf_types<size>::Elf_Addr gp_;
+
+  // Whether the object is a PIC object.
+  bool is_pic_;
+  // Whether the object is using the N32 ABI.
+  bool is_n32_;
+  // Whether the object is using the N64 ABI.
+  bool is_n64_;
+
+  Mips_got_info<size, big_endian>* got_info_;
+};
+
+// Mips_output_data_got class.
+
+template<int size, bool big_endian>
+class Mips_output_data_got : public Output_data_got<size, big_endian>
+{
+ public:
+  typedef Output_data_reloc<elfcpp::SHT_REL, true, size, big_endian>
+    Reloc_section;
+
+  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), global_got_index_(-1U),
+      multi_got_(false)
+  {
+    this->got_info = new Mips_got_info<size, big_endian>();
+    this->set_addralign(16);
+  }
+
+  // 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, Symbol* 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,
+                          Symbol* gsym)
+  {
+    this->secondary_got_relocs_.push_back(Static_reloc(got_offset,
+                                                       r_type, gsym));
+  }
+
+  unsigned char*
+  got_view()
+  { return this->got_view_;  }
+
+  Symbol_table*
+  symbol_table()
+  { return this->symbol_table_;  }
+
+  Layout*
+  layout()
+  { return this->layout_;  }
+
+  // Reserve space in G for a GOT entry containing the value of symbol
+  // SYMNDX in input object OBJECT, plus ADDEND.
+  void
+  record_local_got_symbol(Sized_relobj_file<size, big_endian>* object,
+                          long symndx,
+                          typename elfcpp::Elf_types<size>::Elf_Addr addend,
+                          unsigned int r_type,
+                          unsigned int shndx)
+  {
+    this->got_info->record_local_got_symbol(object, symndx, addend, r_type,
+                                            shndx);
+  }
+
+  void
+  record_global_got_symbol(Mips_symbol<size>* mips_sym,
+                           Sized_relobj_file<size, big_endian>* object,
+                           unsigned int r_type, bool reloc, bool for_call)
+  {
+    this->got_info->record_global_got_symbol(mips_sym, object, r_type, reloc,
+                                             for_call);
+  }
+
+  unsigned int
+  local_gotno() const
+  {
+    if (!this->multi_got_)
+      return this->got_info->local_gotno();
+    else
+      {
+        Mips_got_info<size, big_endian> *primary = this->got_info->next();
+        return 2 + primary->local_gotno() + primary->page_gotno();
+      }
+  }
+
+  unsigned int
+  global_got_index() const
+  { return this->global_got_index_; }
+
+  void
+  set_global_got_index(unsigned int global_got_index)
+  { this->global_got_index_ = global_got_index; }
+
+  void
+  lay_out_got(Target_mips<size, big_endian> *target, Layout* layout,
+              Symbol_table *symtab, const Input_objects* input_objects)
+  { this->got_info->lay_out_got(target, layout, symtab, input_objects); }
+
+  unsigned int
+  get_got_page_offset(unsigned int value,
+                      const Sized_relobj_file<size, big_endian> *object)
+  {
+    if (!this->multi_got_)
+      return this->got_info->get_got_page_offset(value, this->got_view());
+    else
+      {
+        Mips_got_info<size, big_endian> *g =
+          this->got_info->get_got_info(object, false);
+        return g->get_got_page_offset(value, this->got_view());
+      }
+  }
+
+  // Record that OBJECT has a page relocation against symbol SYMNDX and
+  // that ADDEND is the addend for that relocation.
+  void
+  record_got_page_entry(Sized_relobj_file<size, big_endian> *object,
+                        unsigned int symndx, int addend)
+  { this->got_info->record_got_page_entry(object, symndx, addend); }
+
+  unsigned int got_offset(const Symbol* gsym, unsigned int got_type,
+                          Sized_relobj_file<size, big_endian>* object) const
+  {
+    if (!this->multi_got_)
+      return gsym->got_offset(got_type);
+    else
+      {
+        Mips_got_info<size, big_endian> *g =
+          this->got_info->get_got_info(object, false);
+        return gsym->got_offset(this->multigot_got_type(got_type) + g->index());
+      }
+  }
+
+  unsigned int
+  tls_ldm_offset(Sized_relobj_file<size, big_endian>* object) const
+  {
+    if (!this->multi_got_)
+      return this->got_info->tls_ldm_offset();
+    else
+      {
+        Mips_got_info<size, big_endian> *g =
+          this->got_info->get_got_info(object, false);
+        return g->tls_ldm_offset();
+      }
+  }
+
+  void
+  set_tls_ldm_offset(unsigned int tls_ldm_offset,
+                     Sized_relobj_file<size, big_endian>* object)
+  {
+    if (!this->multi_got_)
+      this->got_info->set_tls_ldm_offset(tls_ldm_offset);
+    else
+      {
+        Mips_got_info<size, big_endian> *g =
+          this->got_info->get_got_info(object, false);
+        g->set_tls_ldm_offset(tls_ldm_offset);
+      }
+  }
+
+  unsigned int
+  multigot_got_type(unsigned int got_type) const
+  {
+    switch (got_type)
+      {
+      case GOT_TYPE_STANDARD:
+        return GOT_TYPE_STANDARD_MULTIGOT;
+      case GOT_TYPE_TLS_OFFSET:
+        return GOT_TYPE_TLS_OFFSET_MULTIGOT;
+      case GOT_TYPE_TLS_PAIR:
+        return GOT_TYPE_TLS_PAIR_MULTIGOT;
+      default:
+        gold_unreachable();
+      }
+  }
+
+  // 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) const
+  { return object->local_got_offset(symndx, got_type); }
+
+  bool
+  multi_got() const
+  { return this->multi_got_; }
+
+  void
+  set_multi_got()
+  { this->multi_got_ = true; }
+
+  unsigned int
+  get_got_offset(const Sized_relobj_file<size, big_endian>* object)
+  {
+    if (!this->multi_got_)
+      return 0;
+    else
+      {
+        Mips_got_info<size, big_endian> *g =
+          this->got_info->get_got_info(object, false);
+        return g != NULL ? g->offset() : 0;
+      }
+  }
+
+  void
+  add_reloc_only_entries()
+  { this->got_info->add_reloc_only_entries(this); }
+
+ 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, Symbol* 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.
+    Symbol*
+    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.
+        Symbol* 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_;
+  };
+
+  Target_mips<size, big_endian> *target_;
+
+  // Symbol table of the output object.
+  Symbol_table* symbol_table_;
+  // Layout of the output object.
+  Layout* layout_;
+
+  // Static relocs to be applied to the GOT.
+  std::vector<Static_reloc> static_relocs_;
+
+  // .got section view
+  unsigned char* got_view_;
+
+  unsigned int global_got_index_;
+
+  // The master GOT information.
+  Mips_got_info<size, big_endian> *got_info;
+
+  bool multi_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
+{
+ public:
+  Mips_output_data_la25_stub()
+  : Output_section_data(size == 32 ? 4 : 8)
+  { }
+
+  // Add LA25 stub for a symbol.
+  void
+  add_la25_stub(Symbol_table* symtab, Target_mips<size, big_endian>* target,
+                Symbol* gsym);
+
+ protected:
+  void
+  do_adjust_output_section(Output_section* os)
+  { os->set_entsize(0); }
+
+ private:
+  // Template for LA25 stub.
+  static const uint32_t la25_stub_entry[4];
+
+  // Set the final size.
+  void
+  set_final_data_size()
+  { this->set_data_size(this->symbols_.size() * 16); }
+
+  // Creates a symbol for the MIPS_SYM's stub's value and size, to help make
+  // the disassembly easier to read.
+  Symbol*
+  create_stub_symbol(Mips_symbol<size>* mips_sym, Symbol_table* symtab,
+                     Target_mips<size, big_endian>* target, uint64_t symsize);
+
+  // Write out the LA25 stub section.
+  void
+  do_write(Output_file*);
+
+  // Symbols that have stubs.
+  Unordered_set<Symbol*> symbols_;
+};
+
+// A class to handle the PLT data.
+
+template<int size, bool big_endian>
+class Mips_output_data_plt : public Output_section_data
+{
+ public:
+  typedef Output_data_reloc<elfcpp::SHT_REL, true,
+                            size, big_endian> Reloc_section;
+
+  Mips_output_data_plt(Layout*, Output_data_space*);
+
+  // Add an entry to the PLT.
+  void
+  add_entry(Symbol* gsym);
+
+  // 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->count_; }
+
+  // Return the offset of the first non-reserved PLT entry.
+  static unsigned int
+  first_plt_entry_offset()
+  { return sizeof(plt0_entry_o32); }
+
+  // Return the size of a PLT entry.
+  static unsigned int
+  plt_entry_size()
+  { return sizeof(plt_entry); }
+
+ protected:
+  void
+  do_adjust_output_section(Output_section* os);
+
+  // 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[8];
+  static const uint32_t plt0_entry_n32[8];
+  static const uint32_t plt0_entry_n64[8];
+
+  // Template for subsequent PLT entries.
+  static const uint32_t plt_entry[4];
+
+  // Set the final size.
+  void
+  set_final_data_size()
+  {
+    unsigned int full_count = this->count_ * 4 + 8;
+    this->set_data_size(full_count * 4);
+  }
+
+  // Write out the PLT data.
+  void
+  do_write(Output_file*);
+
+  // The reloc section.
+  Reloc_section* rel_;
+  // The .got.plt section.
+  Output_data_space* got_plt_;
+  // The number of PLT entries.
+  unsigned int count_;
+};
+
+// A class to handle the .MIPS.stubs data.
+
+template<int size, bool big_endian>
+class Mips_output_data_mips_stubs : public Output_section_data
+{
+ public:
+   Mips_output_data_mips_stubs()
+     : Output_section_data(size == 32 ? 4 : 8), dynsym_count_(-1U),
+       stub_offsets_are_set_(false)
+   { }
+
+  // Add an entry to the .MIPS.stubs.
+  void
+  add_entry(Symbol* gsym);
+
+  // Remove entry for a symbol.
+  void
+  remove_entry(Symbol *sym);
+
+  void
+  set_lazy_stub_offsets();
+
+  void
+  set_needs_dynsym_value();
+
+  void
+  set_dynsym_count(unsigned int dynsym_count)
+  { this->dynsym_count_ = dynsym_count; }
+
+ protected:
+  void
+  do_adjust_output_section(Output_section* os);
+
+  // 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[4];
+  static const uint32_t lazy_stub_normal_1_n64[4];
+  static const uint32_t lazy_stub_normal_2[4];
+  static const uint32_t lazy_stub_normal_2_n64[4];
+  static const uint32_t lazy_stub_big[5];
+  static const uint32_t lazy_stub_big_n64[5];
+
+  // Set the final size.
+  void
+  set_final_data_size()
+  { this->set_data_size(this->symbols_.size() * 20); }
+
+  // Write out the .MIPS.stubs data.
+  void
+  do_write(Output_file*);
+
+  // .MIPS.stubs symbols
+  Unordered_set<Symbol*> symbols_;
+  unsigned int dynsym_count_;
+  bool stub_offsets_are_set_;
+};
+
+// This class handles Mips .reginfo output section.
+
+template<int size, bool big_endian>
+class Mips_output_section_reginfo : public Output_section
+{
+ public:
+  Mips_output_section_reginfo (const char *name, elfcpp::Elf_Word type,
+                               elfcpp::Elf_Xword flags,
+                               Target_mips<size, big_endian>* target)
+    : Output_section(name, type, flags), target_(target)
+  {
+    this->set_always_keeps_input_sections();
+  }
+
+  // Downcast a base pointer to an Mips_output_section_reginfo pointer.
+  static Mips_output_section_reginfo<size, big_endian>*
+  as_mips_output_section_reginfo(Output_section* os)
+  { return static_cast<Mips_output_section_reginfo<size, big_endian>*>(os); }
+
+ protected:
+  // Set the final data size.
+  void
+  set_final_data_size()
+  { this->set_data_size(24); }
+
+  // Write out reginfo section.
+  void
+  do_write(Output_file * of);
+
+ private:
+  Target_mips<size, big_endian>* target_;
+};
+
+// 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, int sh_type, typename Classify_reloc>
+class Mips_scan_relocatable_relocs :
+  public Default_scan_relocatable_relocs<sh_type, 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 (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<sh_type, Classify_reloc>::
+                local_section_strategy(r_type, object);
+          }
+      }
+  }
+};
+
+// The ""'s around str ensure str is a string literal, so sizeof works.
+#define strprefix(var, str)   (strncmp(var, str, sizeof("" str "") - 1) == 0)
+
+// Return true if this symbol should be added to the dynamic symbol
+// table.  This function is identical to Symbol::should_add_dynsym_entry.
+
+static bool
+should_add_dynsym_entry(Symbol* sym, Symbol_table* symtab)
+{
+  // If the symbol is only present on plugin files, the plugin decided we
+  // don't need it.
+  if (!sym->in_real_elf())
+    return false;
+
+  // If the symbol is used by a dynamic relocation, we need to add it.
+  if (sym->needs_dynsym_entry())
+    return true;
+
+  // If this symbol's section is not added, the symbol need not be added.
+  // The section may have been GCed.  Note that export_dynamic is being
+  // overridden here.  This should not be done for shared objects.
+  if (parameters->options().gc_sections()
+      && !parameters->options().shared()
+      && sym->source() == Symbol::FROM_OBJECT
+      && !sym->object()->is_dynamic())
+    {
+      Relobj* relobj = static_cast<Relobj*>(sym->object());
+      bool is_ordinary;
+      unsigned int shndx = sym->shndx(&is_ordinary);
+      if (is_ordinary && shndx != elfcpp::SHN_UNDEF
+          && !relobj->is_section_included(shndx)
+          && !symtab->is_section_folded(relobj, shndx))
+        return false;
+    }
+
+  // If the symbol was forced dynamic in a --dynamic-list file
+  // or an --export-dynamic-symbol option, add it.
+  if (!sym->is_from_dynobj()
+      && (parameters->options().in_dynamic_list(sym->name())
+	  || parameters->options().is_export_dynamic_symbol(sym->name())))
+    {
+      if (!sym->is_forced_local())
+        return true;
+      gold_warning(_("Cannot export local symbol '%s'"),
+		   sym->demangled_name().c_str());
+      return false;
+    }
+
+  // If the symbol was forced local in a version script, do not add it.
+  if (sym->is_forced_local())
+    return false;
+
+  // If dynamic-list-data was specified, add any STT_OBJECT.
+  if (parameters->options().dynamic_list_data()
+      && !sym->is_from_dynobj()
+      && sym->type() == elfcpp::STT_OBJECT)
+    return true;
+
+  // If --dynamic-list-cpp-new was specified, add any new/delete symbol.
+  // If --dynamic-list-cpp-typeinfo was specified, add any typeinfo symbols.
+  if ((parameters->options().dynamic_list_cpp_new()
+       || parameters->options().dynamic_list_cpp_typeinfo())
+      && !sym->is_from_dynobj())
+    {
+      char* demangled_name = cplus_demangle(sym->name(),
+                                            DMGL_ANSI | DMGL_PARAMS);
+      if (demangled_name == NULL)
+        {
+          // Not a C++ symbol, so it can't satisfy these flags
+        }
+      else if (parameters->options().dynamic_list_cpp_new()
+               && (strprefix(demangled_name, "operator new")
+                   || strprefix(demangled_name, "operator delete")))
+        {
+          free(demangled_name);
+          return true;
+        }
+      else if (parameters->options().dynamic_list_cpp_typeinfo()
+               && (strprefix(demangled_name, "typeinfo name for")
+                   || strprefix(demangled_name, "typeinfo for")))
+        {
+          free(demangled_name);
+          return true;
+        }
+      else
+        free(demangled_name);
+    }
+
+  // If exporting all symbols or building a shared library,
+  // and the symbol is defined in a regular object and is
+  // externally visible, we need to add it.
+  if ((parameters->options().export_dynamic() || parameters->options().shared())
+      && !sym->is_from_dynobj()
+      && !sym->is_undefined()
+      && sym->is_externally_visible())
+    return true;
+
+  return false;
+}
+
+// Mips_copy_relocs class. This class is almost identical to the class
+// Copy_relocs from copy-relocs.h. The only difference is method
+// Copy_reloc_entry::emit.
+
+// This class is used to manage COPY relocations.  We try to avoid
+// them when possible.  A COPY relocation may be required when an
+// executable refers to a variable defined in a shared library.  COPY
+// relocations are problematic because they tie the executable to the
+// exact size of the variable in the shared library.  We can avoid
+// them if all the references to the variable are in a writeable
+// section.  In that case we can simply use dynamic relocations.
+// However, when scanning relocs, we don't know when we see the
+// relocation whether we will be forced to use a COPY relocation or
+// not.  So we have to save the relocation during the reloc scanning,
+// and then emit it as a dynamic relocation if necessary.  This class
+// implements that.  It is used by the target specific code.
+
+// The template parameter SH_TYPE is the type of the reloc section to
+// be used for COPY relocs: elfcpp::SHT_REL or elfcpp::SHT_RELA.
+
+template<int sh_type, int size, bool big_endian>
+class Mips_copy_relocs
+{
+ private:
+  typedef typename Reloc_types<sh_type, size, big_endian>::Reloc Reloc;
+
+ public:
+  Mips_copy_relocs(unsigned int copy_reloc_type)
+    : copy_reloc_type_(copy_reloc_type), dynbss_(NULL), entries_()
+  { }
+
+  // This is called while scanning relocs if we see a relocation
+  // against a symbol which may force us to generate a COPY reloc.
+  // SYM is the symbol.  OBJECT is the object whose relocs we are
+  // scanning.  The relocation is being applied to section SHNDX in
+  // OBJECT.  OUTPUT_SECTION is the output section where section SHNDX
+  // will wind up.  REL is the reloc itself.  The Output_data_reloc
+  // section is where the dynamic relocs are put.
+  void
+  copy_reloc(Symbol_table*, Layout*, Sized_symbol<size>* sym,
+             Sized_relobj_file<size, big_endian>* object,
+             unsigned int shndx, Output_section* output_section,
+             const Reloc& rel,
+             Output_data_reloc<sh_type, true, size, big_endian>*);
+
+  // Return whether there are any saved relocations.
+  bool
+  any_saved_relocs() const
+  { return !this->entries_.empty(); }
+
+  // Emit any saved relocations which turn out to be needed.  This is
+  // called after all the relocs have been scanned.
+  void
+  emit(Output_data_reloc<sh_type, true, size, big_endian>*,
+       Symbol_table*, Layout*, Target_mips<size, big_endian>*);
+
+  // Emit a COPY reloc.
+  void
+  emit_copy_reloc(Symbol_table*, Sized_symbol<size>*,
+                  Output_data*, off_t,
+                  Output_data_reloc<sh_type, true, size, big_endian>*);
+
+ private:
+  typedef typename elfcpp::Elf_types<size>::Elf_Addr Address;
+  typedef typename elfcpp::Elf_types<size>::Elf_Addr Addend;
+
+  // This POD class holds the relocations we are saving.  We will emit
+  // these relocations if it turns out that the symbol does not
+  // require a COPY relocation.
+  class Copy_reloc_entry
+  {
+   public:
+    Copy_reloc_entry(Symbol* sym, unsigned int reloc_type,
+                     Sized_relobj_file<size, big_endian>* relobj,
+                     unsigned int shndx,
+                     Output_section* output_section,
+                     Address address, Addend addend)
+      : sym_(sym), reloc_type_(reloc_type), relobj_(relobj),
+        shndx_(shndx), output_section_(output_section),
+        address_(address), addend_(addend)
+    { }
+
+    // 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(Output_data_reloc<sh_type, true, size, big_endian>*,
+         Mips_copy_relocs<sh_type, size, big_endian>* copy_relocs,
+         Symbol_table*, Layout*, Target_mips<size, big_endian>*);
+
+   private:
+    Symbol* sym_;
+    unsigned int reloc_type_;
+    Sized_relobj_file<size, big_endian>* relobj_;
+    unsigned int shndx_;
+    Output_section* output_section_;
+    Address address_;
+    Addend addend_;
+  };
+
+  // A list of relocs to be saved.
+  typedef std::vector<Copy_reloc_entry> Copy_reloc_entries;
+
+  // Return whether we need a COPY reloc.
+  bool
+  need_copy_reloc(Sized_symbol<size>* gsym,
+                  Sized_relobj_file<size, big_endian>* object,
+                  unsigned int shndx) const;
+
+  // Make a new COPY reloc and emit it.
+  void
+  make_copy_reloc(Symbol_table*, Layout*, Sized_symbol<size>*,
+                  Output_data_reloc<sh_type, true, size, big_endian>*);
+
+  // Save a reloc against SYM for possible emission later.
+  void
+  save(Symbol*, Sized_relobj_file<size, big_endian>*, unsigned int shndx,
+       Output_section*, const Reloc& rel);
+
+  // The target specific relocation type of the COPY relocation.
+  const unsigned int copy_reloc_type_;
+  // The dynamic BSS data which goes into the .bss section.  This is
+  // where variables which require COPY relocations are placed.
+  Output_data_space* dynbss_;
+  // The list of relocs we are saving.
+  Copy_reloc_entries entries_;
+};
+
+
+// Mips_copy_relocs::Copy_reloc_entry methods.
+
+// Emit the reloc if appropriate.
+
+template<int sh_type, int size, bool big_endian>
+void
+Mips_copy_relocs<sh_type, size, big_endian>::Copy_reloc_entry::emit(
+    Output_data_reloc<sh_type, true, size, big_endian>* reloc_section,
+    Mips_copy_relocs<sh_type, size, big_endian>* copy_relocs,
+    Symbol_table* symtab, Layout* layout, Target_mips<size, big_endian> *target)
+{
+  // If the symbol is no longer defined in a dynamic object, then we
+  // emitted a COPY relocation, and we do not want to emit this
+  // dynamic relocation.
+  if (!this->sym_->is_from_dynobj())
+    return;
+
+  bool can_make_dynamic = this->reloc_type_ == elfcpp::R_MIPS_32
+                       || this->reloc_type_ == elfcpp::R_MIPS_REL32
+                       || this->reloc_type_ == elfcpp::R_MIPS_64;
+
+  Mips_symbol<size>* sym = Mips_symbol<size>::as_mips_sym(this->sym_);
+  if (can_make_dynamic && !sym->has_static_relocs())
+    {
+      target->got_section(symtab, layout)->record_global_got_symbol(
+                            sym, this->relobj_, this->reloc_type_, true, false);
+      if (!symbol_references_local (sym, should_add_dynsym_entry(sym, symtab)))
+        target->rel_dyn_section(layout)->add_global(sym, elfcpp::R_MIPS_REL32,
+            this->output_section_, this->relobj_, this->shndx_, this->address_);
+      else
+        target->rel_dyn_section(layout)->add_symbolless_global_addend(
+            sym, elfcpp::R_MIPS_REL32, this->output_section_, this->relobj_,
+            this->shndx_, this->address_);
+    }
+  else
+    copy_relocs->make_copy_reloc(symtab, layout,
+        static_cast<Sized_symbol<size>*>(this->sym_), reloc_section);
+}
+
+// Mips_copy_relocs methods.
+
+// Handle a relocation against a symbol which may force us to generate
+// a COPY reloc.
+
+template<int sh_type, int size, bool big_endian>
+void
+Mips_copy_relocs<sh_type, size, big_endian>::copy_reloc(
+    Symbol_table* symtab,
+    Layout* layout,
+    Sized_symbol<size>* sym,
+    Sized_relobj_file<size, big_endian>* object,
+    unsigned int shndx,
+    Output_section* output_section,
+    const Reloc& rel,
+    Output_data_reloc<sh_type, true, size, big_endian>* reloc_section)
+{
+  if (this->need_copy_reloc(sym, object, shndx))
+    this->make_copy_reloc(symtab, layout, sym, reloc_section);
+  else
+    {
+      // We may not need a COPY relocation.  Save this relocation to
+      // possibly be emitted later.
+      this->save(sym, object, shndx, output_section, rel);
+    }
+}
+
+// Return whether we need a COPY reloc for a relocation against SYM.
+// The relocation is begin applied to section SHNDX in OBJECT.
+
+template<int sh_type, int size, bool big_endian>
+bool
+Mips_copy_relocs<sh_type, size, big_endian>::need_copy_reloc(
+    Sized_symbol<size>* sym,
+    Sized_relobj_file<size, big_endian>* object,
+    unsigned int shndx) const
+{
+  if (!parameters->options().copyreloc())
+    return false;
+
+  if (sym->symsize() == 0)
+    return false;
+
+  // If this is a readonly section, then we need a COPY reloc.
+  // Otherwise we can use a dynamic reloc.  Note that calling
+  // section_flags here can be slow, as the information is not cached;
+  // fortunately we shouldn't see too many potential COPY relocs.
+  if ((object->section_flags(shndx) & elfcpp::SHF_WRITE) == 0)
+    return true;
+
+  return false;
+}
+
+// Emit a COPY relocation for SYM.
+
+template<int sh_type, int size, bool big_endian>
+void
+Mips_copy_relocs<sh_type, size, big_endian>::emit_copy_reloc(
+    Symbol_table* symtab,
+    Sized_symbol<size>* sym,
+    Output_data* posd,
+    off_t offset,
+    Output_data_reloc<sh_type, true, size, big_endian>* reloc_section)
+{
+  // Define the symbol as being copied.
+  symtab->define_with_copy_reloc(sym, posd, offset);
+
+  // Add the COPY relocation to the dynamic reloc section.
+  reloc_section->add_global_generic(sym, this->copy_reloc_type_, posd,
+                                    offset, 0);
+}
+
+// Make a COPY relocation for SYM and emit it.
+
+template<int sh_type, int size, bool big_endian>
+void
+Mips_copy_relocs<sh_type, size, big_endian>::make_copy_reloc(
+    Symbol_table* symtab,
+    Layout* layout,
+    Sized_symbol<size>* sym,
+    Output_data_reloc<sh_type, true, size, big_endian>* reloc_section)
+{
+  // We should not be here if -z nocopyreloc is given.
+  gold_assert(parameters->options().copyreloc());
+
+  typename elfcpp::Elf_types<size>::Elf_WXword symsize = sym->symsize();
+
+  // There is no defined way to determine the required alignment of
+  // the symbol.  We know that the symbol is defined in a dynamic
+  // object.  We start with the alignment of the section in which it
+  // is defined; presumably we do not require an alignment larger than
+  // that.  Then we reduce that alignment if the symbol is not aligned
+  // within the section.
+  gold_assert(sym->is_from_dynobj());
+  bool is_ordinary;
+  unsigned int shndx = sym->shndx(&is_ordinary);
+  gold_assert(is_ordinary);
+  typename elfcpp::Elf_types<size>::Elf_WXword addralign;
+
+  {
+    // Lock the object so we can read from it.  This is only called
+    // single-threaded from scan_relocs, so it is OK to lock.
+    // Unfortunately we have no way to pass in a Task token.
+    const Task* dummy_task = reinterpret_cast<const Task*>(-1);
+    Object* obj = sym->object();
+    Task_lock_obj<Object> tl(dummy_task, obj);
+    addralign = obj->section_addralign(shndx);
+  }
+
+  typename Sized_symbol<size>::Value_type value = sym->value();
+  while ((value & (addralign - 1)) != 0)
+    addralign >>= 1;
+
+  // Mark the dynamic object as needed for the --as-needed option.
+  sym->object()->set_is_needed();
+
+  if (this->dynbss_ == NULL)
+    {
+      this->dynbss_ = new Output_data_space(addralign, "** dynbss");
+      layout->add_output_section_data(".bss",
+                                      elfcpp::SHT_NOBITS,
+                                      elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE,
+                                      this->dynbss_, ORDER_BSS, false);
+    }
+
+  Output_data_space* dynbss = this->dynbss_;
+
+  if (addralign > dynbss->addralign())
+    dynbss->set_space_alignment(addralign);
+
+  section_size_type dynbss_size =
+    convert_to_section_size_type(dynbss->current_data_size());
+  dynbss_size = align_address(dynbss_size, addralign);
+  section_size_type offset = dynbss_size;
+  dynbss->set_current_data_size(dynbss_size + symsize);
+
+  this->emit_copy_reloc(symtab, sym, dynbss, offset, reloc_section);
+}
+
+// Save a relocation to possibly be emitted later.
+
+template<int sh_type, int size, bool big_endian>
+void
+Mips_copy_relocs<sh_type, size, big_endian>::save(
+    Symbol* sym,
+    Sized_relobj_file<size, big_endian>* object,
+    unsigned int shndx,
+    Output_section* output_section,
+    const Reloc& rel)
+{
+  unsigned int reloc_type = elfcpp::elf_r_type<size>(rel.get_r_info());
+  typename elfcpp::Elf_types<size>::Elf_Addr addend =
+    Reloc_types<sh_type, size, big_endian>::get_reloc_addend_noerror(&rel);
+  this->entries_.push_back(Copy_reloc_entry(sym, reloc_type, object, shndx,
+                                            output_section, rel.get_r_offset(),
+                                            addend));
+}
+
+// Emit any saved relocs.
+
+template<int sh_type, int size, bool big_endian>
+void
+Mips_copy_relocs<sh_type, size, big_endian>::emit(
+    Output_data_reloc<sh_type, true, size, big_endian>* reloc_section,
+    Symbol_table* symtab, Layout* layout, Target_mips<size, big_endian> *target)
+{
+  for (typename Copy_reloc_entries::iterator p = this->entries_.begin();
+       p != this->entries_.end();
+       ++p)
+    p->emit(reloc_section, this, symtab, layout, target);
+
+  // We no longer need the saved information.
+  this->entries_.clear();
+}
+
+// Instantiate the templates we need.
+
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Mips_copy_relocs<elfcpp::SHT_REL, 32, false>;
+
+template
+class Mips_copy_relocs<elfcpp::SHT_RELA, 32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Mips_copy_relocs<elfcpp::SHT_REL, 32, true>;
+
+template
+class Mips_copy_relocs<elfcpp::SHT_RELA, 32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Mips_copy_relocs<elfcpp::SHT_REL, 64, false>;
+
+template
+class Mips_copy_relocs<elfcpp::SHT_RELA, 64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Mips_copy_relocs<elfcpp::SHT_REL, 64, true>;
+
+template
+class Mips_copy_relocs<elfcpp::SHT_RELA, 64, true>;
+#endif
+
+
+// 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->source() != Symbol::FROM_OBJECT || sym->object()->is_dynamic()
+      || 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>
+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();
+}
+
+// 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 (target_->local_pic_function(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())
+          {
+            target_->create_la25_stub_section(layout_);
+            target_->la25_stub_section()->add_la25_stub(symtab_, target_,
+                                                        mips_sym);
+          }
+      }
+  }
+
+ private:
+  Target_mips<size, big_endian>* target_;
+  Layout* layout_;
+  Symbol_table* symtab_;
+};
+
+template<int size, bool big_endian>
+class Target_mips : public Sized_target<size, big_endian>
+{
+ public:
+  typedef Output_data_reloc<elfcpp::SHT_REL, true, size, big_endian>
+    Reloc_section;
+  typedef Output_data_reloc<elfcpp::SHT_RELA, true, size, big_endian>
+    Reloca_section;
+  typedef Unordered_map<const char*, Symbol*> Stub_symbol_info;
+
+  Target_mips(const Target::Target_info* info = &mips_info)
+    : Sized_target<size, big_endian>(info),
+      got_(NULL), gp_(NULL), plt_(NULL), got_plt_(NULL),
+      rel_dyn_(NULL), rela_dyn_(NULL), copy_relocs_(elfcpp::R_MIPS_COPY),
+      copy_relocsa_(elfcpp::R_MIPS_COPY), la25_stub_(NULL), mips_stubs_(NULL),
+      ei_class_(0), mach_(0), layout_(NULL)
+  {
+    this->add_machine_extensions();
+  }
+
+  // The offset of $gp from the beginning of the .got section.
+  static const unsigned int MIPS_GP_OFFSET = 0x7ff0;
+
+  // The maximum size of the GOT for it to be addressable using 16-bit
+  // offsets from $gp.
+  static const unsigned int MIPS_GOT_MAX_SIZE = MIPS_GP_OFFSET + 0x7fff;
+
+  // Make a new symbol table entry for the Mips target.
+  Sized_symbol<size>*
+  make_symbol() const
+  { return new Mips_symbol<size>(); }
+
+  // Process the relocations to determine unreferenced sections for
+  // garbage collection.
+  void
+  gc_process_relocs(Symbol_table* symtab,
+                    Layout* layout,
+                    Sized_relobj_file<size, big_endian>* object,
+                    unsigned int data_shndx,
+                    unsigned int sh_type,
+                    const unsigned char* prelocs,
+                    size_t reloc_count,
+                    Output_section* output_section,
+                    bool needs_special_offset_handling,
+                    size_t local_symbol_count,
+                    const unsigned char* plocal_symbols);
+
+  // Scan the relocations to look for symbol adjustments.
+  void
+  scan_relocs(Symbol_table* symtab,
+              Layout* layout,
+              Sized_relobj_file<size, big_endian>* object,
+              unsigned int data_shndx,
+              unsigned int sh_type,
+              const unsigned char* prelocs,
+              size_t reloc_count,
+              Output_section* output_section,
+              bool needs_special_offset_handling,
+              size_t local_symbol_count,
+              const unsigned char* plocal_symbols);
+
+  // Finalize the sections.
+  void
+  do_finalize_sections(Layout *, const Input_objects *, Symbol_table *);
+
+  // Relocate a section.
+  void
+  relocate_section(const Relocate_info<size, big_endian>*,
+                   unsigned int sh_type,
+                   const unsigned char* prelocs,
+                   size_t reloc_count,
+                   Output_section* output_section,
+                   bool needs_special_offset_handling,
+                   unsigned char* view,
+                   typename elfcpp::Elf_types<size>::Elf_Addr view_address,
+                   section_size_type view_size,
+                   const Reloc_symbol_changes*);
+
+  // Scan the relocs during a relocatable link.
+  void
+  scan_relocatable_relocs(Symbol_table* symtab,
+                          Layout* layout,
+                          Sized_relobj_file<size, big_endian>* object,
+                          unsigned int data_shndx,
+                          unsigned int sh_type,
+                          const unsigned char* prelocs,
+                          size_t reloc_count,
+                          Output_section* output_section,
+                          bool needs_special_offset_handling,
+                          size_t local_symbol_count,
+                          const unsigned char* plocal_symbols,
+                          Relocatable_relocs*);
+
+  // Emit relocations for a section during a relocatable link, and for
+  // --emit-relocs.
+  void
+  relocate_relocs(const Relocate_info<size, big_endian>*,
+                  unsigned int sh_type,
+                  const unsigned char* prelocs,
+                  size_t reloc_count,
+                  Output_section* output_section,
+                  typename elfcpp::Elf_types<size>::Elf_Off
+                    offset_in_output_section,
+                  const Relocatable_relocs*,
+                  unsigned char* view,
+                  typename elfcpp::Elf_types<size>::Elf_Addr view_address,
+                  section_size_type view_size,
+                  unsigned char* reloc_view,
+                  section_size_type reloc_view_size);
+
+  // Perform target-specific processing in a relocatable link.  This is
+  // only used if we use the relocation strategy RELOC_SPECIAL.
+  void
+  relocate_special_relocatable(const Relocate_info<size, big_endian>* relinfo,
+                               unsigned int sh_type,
+                               const unsigned char* preloc_in,
+                               size_t relnum,
+                               Output_section* output_section,
+                               typename elfcpp::Elf_types<size>::Elf_Off
+                                 offset_in_output_section,
+                               unsigned char* view,
+                               typename elfcpp::Elf_types<32>::Elf_Addr
+                                 view_address,
+                               section_size_type view_size,
+                               unsigned char* preloc_out);
+
+  // Return whether SYM is defined by the ABI.
+  bool
+  do_is_defined_by_abi(const Symbol* sym) const
+  {
+    return ((strcmp(sym->name(), "__gnu_local_gp") == 0)
+            || (strcmp(sym->name(), "_gp_disp") == 0)
+            || (strcmp(sym->name(), "___tls_get_addr") == 0));
+  }
+
+  // Return the number of entries in the GOT.
+  unsigned int
+  got_entry_count() const
+  {
+    if (!this->has_got_section())
+      return 0;
+    return this->got_size() / 4;
+  }
+
+  // Return the number of entries in the PLT.
+  unsigned int
+  plt_entry_count() const
+  {
+    if (this->plt_ == NULL)
+      return 0;
+    return this->plt_->entry_count();
+  }
+
+  // Return the offset of the first non-reserved PLT entry.
+  unsigned int
+  first_plt_entry_offset() const
+  { return Mips_output_data_plt<size, big_endian>::first_plt_entry_offset(); }
+
+  // Return the size of each PLT entry.
+  unsigned int
+  plt_entry_size() const
+  { return Mips_output_data_plt<size, big_endian>::plt_entry_size(); }
+
+  // Returns la25 stub section.
+  Mips_output_data_la25_stub<size, big_endian>*
+  la25_stub_section()
+  { return this->la25_stub_; }
+
+  // Return .MIPS.stubs output section.
+  Mips_output_data_mips_stubs<size, big_endian>*
+  mips_stubs() const
+  { return this->mips_stubs_; }
+
+  // Return TRUE if a relocation of type R_TYPE from MIPS_RELOBJ might
+  // require an la25 stub.  See also local_pic_function, which determines
+  // whether the destination function ever requires a stub.
+  bool
+  relocation_needs_la25_stub(Mips_relobj<size, big_endian>* mips_relobj,
+                              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 (mips_relobj->is_pic())
+      return false;
+
+    switch (r_type)
+      {
+      case elfcpp::R_MIPS_26:
+      case elfcpp::R_MIPS_PC16:
+      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.
+  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>* mips_relobj =
+          static_cast<Mips_relobj<size, big_endian>*>(sym->object());
+
+        if ((mips_relobj->is_pic() || sym->is_pic())
+            && (!sym->is_mips16()
+                || (this->get_mips16_fn_stub(sym) != NULL
+                    && sym->need_fn_stub())))
+          return true;
+      }
+    return false;
+  }
+
+  // Create LA25 stub section.
+  void
+  create_la25_stub_section(Layout*);
+
+  // Get the GOT section, creating it if necessary.
+  Mips_output_data_got<size, big_endian>*
+  got_section(Symbol_table*, Layout*);
+
+  // Get the GOT section.
+  Mips_output_data_got<size, big_endian>*
+  got_section() const
+  {
+    gold_assert(this->got_ != NULL);
+    return this->got_;
+  }
+
+  // Get value of gp.
+  const typename elfcpp::Elf_types<size>::Elf_Addr
+  gp_value() const
+  {
+    if (this->gp_ != NULL)
+      return this->gp_->value();
+    return 0;
+  }
+
+  // Get value of gp. Adjust it for multi-got links.
+  typename elfcpp::Elf_types<size>::Elf_Addr
+  adjusted_gp_value(const Sized_relobj_file<size, big_endian>* object)
+  {
+    if (this->gp_ == NULL)
+      return 0;
+
+    bool multi_got = false;
+    if (this->has_got_section())
+      multi_got = this->got_section()->multi_got();
+    if (!multi_got)
+      return this->gp_->value();
+    else
+      return this->gp_->value() + this->got_section()->get_got_offset(object);
+  }
+
+  // Get the dynamic reloc section, creating it if necessary.
+  Reloc_section*
+  rel_dyn_section(Layout*);
+
+  Reloca_section*
+  rela_dyn_section(Layout*);
+
+  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);
+  }
+
+  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);
+  }
+
+  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
+  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
+  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;
+      }
+  }
+
+  // Return TRUE if relocations in section SECTION_NAME can refer directly to a
+  // MIPS16 function rather than to a hard-float stub.
+  static bool
+  section_allows_mips16_refs(const char *section_name)
+  {
+    return (is_prefix_of(".mips16.fn", section_name)
+            || is_prefix_of(".mips16.call", section_name)
+            || is_prefix_of(".mips16.call.fp", section_name)
+            || strcmp (section_name, ".pdr") == 0);
+  }
+
+  bool
+  custom_set_dynsym_indexes() const
+  { return true; }
+
+  void
+  reorder_dyn_symbols(std::vector<Symbol*>* dyn_symbols,
+                      std::vector<Symbol*>* non_got_symbols,
+                      std::vector<Symbol*>* got_symbols) const
+  {
+    // Mips ABI requires that symbols that have .got entry be at the end of
+    // dynamic symbol table, and that order matches order in .got.
+    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>);
+  }
+
+  // Set the dynamic symbol indexes.  INDEX is the index of the first
+  // global dynamic symbol.  Pointers to the symbols are stored into the
+  // vector SYMS.  The names are added to DYNPOOL.  This returns an
+  // updated dynamic symbol index.
+
+  unsigned int
+  set_dynsym_indexes(std::vector<Symbol*>* dyn_symbols, unsigned int index,
+                     std::vector<Symbol*>* syms, Stringpool* dynpool,
+                     Versions* versions, Symbol_table* symtab) const
+  {
+    std::vector<Symbol *> non_got_symbols;
+    std::vector<Symbol *> got_symbols;
+
+    reorder_dyn_symbols(dyn_symbols, &non_got_symbols, &got_symbols);
+
+    for (std::vector<Symbol *>::iterator p = non_got_symbols.begin();
+         p != non_got_symbols.end();
+         ++p)
+      {
+        Symbol* sym = *p;
+
+        // Note that SYM may already have a dynamic symbol index, since
+        // some symbols appear more than once in the symbol table, with
+        // and without a version.
+
+        if (!sym->has_dynsym_index())
+          {
+            sym->set_dynsym_index(index);
+            ++index;
+            syms->push_back(sym);
+            dynpool->add(sym->name(), false, NULL);
+
+            // Record any version information.
+            if (sym->version() != NULL)
+              versions->record_version(symtab, dynpool, sym);
+
+            // If the symbol is defined in a dynamic object and is
+            // referenced in a regular object, then mark the dynamic
+            // object as needed.  This is used to implement --as-needed.
+            if (sym->is_from_dynobj() && sym->in_reg())
+              sym->object()->set_is_needed();
+          }
+      }
+
+    for (std::vector<Symbol *>::iterator p = got_symbols.begin();
+         p != got_symbols.end();
+         ++p)
+      {
+        Symbol* sym = *p;
+        if (!sym->has_dynsym_index())
+          {
+            // Record any version information.
+            if (sym->version() != NULL)
+              versions->record_version(symtab, dynpool, sym);
+          }
+      }
+
+    index = versions->finalize(symtab, index, syms);
+
+    int got_sym_count = 0;
+    for (std::vector<Symbol *>::iterator p = got_symbols.begin();
+         p != got_symbols.end();
+         ++p)
+      {
+        Symbol* sym = *p;
+
+        if (!sym->has_dynsym_index())
+          {
+            ++got_sym_count;
+            sym->set_dynsym_index(index);
+            ++index;
+            syms->push_back(sym);
+            dynpool->add(sym->name(), false, NULL);
+
+            // If the symbol is defined in a dynamic object and is
+            // referenced in a regular object, then mark the dynamic
+            // object as needed.  This is used to implement --as-needed.
+            if (sym->is_from_dynobj() && sym->in_reg())
+              sym->object()->set_is_needed();
+          }
+      }
+
+    // Set index of the first symbol that has .got entry.
+    this->got_->set_global_got_index(
+      got_sym_count > 0 ? index - got_sym_count : -1U);
+
+    if (this->mips_stubs_ != NULL)
+      this->mips_stubs_->set_dynsym_count(index);
+
+    return index;
+  }
+
+  // Remove symbol that should not have lazy stub.
+  void
+  remove_lazy_stub_entry(Symbol *sym)
+  {
+    if (this->mips_stubs_ != NULL)
+      this->mips_stubs_->remove_entry(sym);
+  }
+
+  // The value to write into got[1] for SVR4 targets, to identify it is
+  // a GNU object.  The dynamic linker can then use got[1] to store the
+  // module pointer.
+  uint64_t
+  mips_elf_gnu_got1_mask()
+  {
+    if (elfcpp::abi_64(this->ei_class_))
+      return (uint64_t)1 << 63;
+    else
+      return 1 << 31;
+  }
+
+  const char*
+  entry_symbol_name() const
+  { return "__start"; }
+
+ protected:
+  // Return the value to use for a dynamic symbol which requires special
+  // treatment.  This is how we support equality comparisons of function
+  // pointers across shared library boundaries, as described in the
+  // processor specific ABI supplement.
+  uint64_t
+  do_dynsym_value(const Symbol* gsym) const;
+
+  // Make an ELF object.
+  Object*
+  do_make_elf_object(const std::string&, Input_file*, off_t,
+                     const elfcpp::Ehdr<size, big_endian>& ehdr);
+
+  Object*
+  do_make_elf_object(const std::string&, Input_file*, off_t,
+                     const elfcpp::Ehdr<size, !big_endian>&)
+  { gold_unreachable(); }
+
+  // Make an output section.
+  Output_section*
+  do_make_output_section(const char *name, elfcpp::Elf_Word type,
+                          elfcpp::Elf_Xword flags)
+    {
+      if (type == elfcpp::SHT_MIPS_REGINFO)
+        return new Mips_output_section_reginfo<size, big_endian>(name, type,
+                                                                 flags, this);
+      else
+        return new Output_section(name, type, flags);
+    }
+
+  void
+  do_adjust_elf_header(unsigned char* view, int len) const;
+
+  // Get the custom dynamic tag value.
+  unsigned int
+  dynamic_tag_custom_value(elfcpp::DT) const;
+
+ private:
+  // The class which scans relocations.
+  class Scan
+  {
+   public:
+    Scan()
+    { }
+
+    static inline int
+    get_reference_flags(unsigned int r_type);
+
+    inline void
+    local(Symbol_table* symtab, Layout* layout, Target_mips* target,
+          Sized_relobj_file<size, big_endian>* object,
+          unsigned int data_shndx,
+          Output_section* output_section,
+          const elfcpp::Rel<size, big_endian>& reloc, unsigned int r_type,
+          const elfcpp::Sym<size, big_endian>& lsym,
+          bool is_discarded);
+
+    inline void
+    local(Symbol_table* symtab, Layout* layout, Target_mips* target,
+          Sized_relobj_file<size, big_endian>* object,
+          unsigned int data_shndx,
+          Output_section* output_section,
+          const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type,
+          const elfcpp::Sym<size, big_endian>& lsym,
+          bool is_discarded);
+
+    inline void
+    local(Symbol_table* symtab, Layout* layout, Target_mips* target,
+          Sized_relobj_file<size, big_endian>* object,
+          unsigned int data_shndx,
+          Output_section* output_section,
+          const elfcpp::Rela<size, big_endian>* rela,
+          const elfcpp::Rel<size, big_endian>* rel,
+          unsigned int rel_type,
+          unsigned int r_type,
+          const elfcpp::Sym<size, big_endian>& lsym,
+          bool is_discarded);
+
+    inline void
+    global(Symbol_table* symtab, Layout* layout, Target_mips* target,
+           Sized_relobj_file<size, big_endian>* object,
+           unsigned int data_shndx,
+           Output_section* output_section,
+           const elfcpp::Rel<size, big_endian>& reloc, unsigned int r_type,
+           Symbol* gsym);
+
+    inline void
+    global(Symbol_table* symtab, Layout* layout, Target_mips* target,
+           Sized_relobj_file<size, big_endian>* object,
+           unsigned int data_shndx,
+           Output_section* output_section,
+           const elfcpp::Rela<size, big_endian>& reloc, unsigned int r_type,
+           Symbol* gsym);
+
+    inline void
+    global(Symbol_table* symtab, Layout* layout, Target_mips* target,
+           Sized_relobj_file<size, big_endian>* object,
+           unsigned int data_shndx,
+           Output_section* output_section,
+           const elfcpp::Rela<size, big_endian>* rela,
+           const elfcpp::Rel<size, big_endian>* rel,
+           unsigned int rel_type,
+           unsigned int r_type,
+           Symbol* gsym);
+
+    inline bool
+    local_reloc_may_be_function_pointer(Symbol_table* , Layout*,
+                                        Target_mips*,
+                                        Sized_relobj_file<size, big_endian>*,
+                                        unsigned int,
+                                        Output_section*,
+                                        const elfcpp::Rel<size, big_endian>&,
+                                        unsigned int,
+                                        const elfcpp::Sym<size, big_endian>&)
+    { return false; }
+
+    inline bool
+    global_reloc_may_be_function_pointer(Symbol_table*, Layout*,
+                                         Target_mips*,
+                                         Sized_relobj_file<size, big_endian>*,
+                                         unsigned int,
+                                         Output_section*,
+                                         const elfcpp::Rel<size, big_endian>&,
+                                         unsigned int, Symbol*)
+    { return false; }
+
+    inline bool
+    local_reloc_may_be_function_pointer(Symbol_table*, Layout*,
+                                        Target_mips*,
+                                        Sized_relobj_file<size, big_endian>*,
+                                        unsigned int,
+                                        Output_section*,
+                                        const elfcpp::Rela<size, big_endian>&,
+                                        unsigned int,
+                                        const elfcpp::Sym<size, big_endian>&)
+    { return false; }
+
+    inline bool
+    global_reloc_may_be_function_pointer(Symbol_table*, Layout*,
+                                         Target_mips*,
+                                         Sized_relobj_file<size, big_endian>*,
+                                         unsigned int,
+                                         Output_section*,
+                                         const elfcpp::Rela<size, big_endian>&,
+                                         unsigned int, Symbol*)
+    { return false; }
+   private:
+    static void
+    unsupported_reloc_local(Sized_relobj_file<size, big_endian>*,
+                            unsigned int r_type);
+
+    static void
+    unsupported_reloc_global(Sized_relobj_file<size, big_endian>*,
+                             unsigned int r_type, Symbol*);
+  };
+
+  // The class which implements relocation.
+  class Relocate
+  {
+   public:
+    Relocate()
+    { }
+
+    ~Relocate()
+    { }
+
+    // Return whether the R_MIPS_32 relocation needs to be applied.
+    inline bool
+    should_apply_r_mips_32_reloc(const Mips_symbol<size>* gsym,
+                                 unsigned int r_type,
+                                 Output_section* output_section,
+                                 Target_mips* target);
+
+    // Do a relocation.  Return false if the caller should not issue
+    // any warnings about this relocation.
+    inline bool
+    relocate(const Relocate_info<size, big_endian>*, Target_mips*,
+             Output_section*, size_t relnum,
+             const elfcpp::Rela<size, big_endian>*,
+             const elfcpp::Rel<size, big_endian>*,
+             unsigned int,
+             unsigned int,  const Sized_symbol<size>*,
+             const Symbol_value<size>*,
+             unsigned char*,
+             typename elfcpp::Elf_types<size>::Elf_Addr,
+             section_size_type);
+
+    inline bool
+    relocate(const Relocate_info<size, big_endian>*, Target_mips*,
+             Output_section*, size_t relnum,
+             const elfcpp::Rel<size, big_endian>&,
+             unsigned int, const Sized_symbol<size>*,
+             const Symbol_value<size>*,
+             unsigned char*,
+             typename elfcpp::Elf_types<size>::Elf_Addr,
+             section_size_type);
+
+    inline bool
+    relocate(const Relocate_info<size, big_endian>*, Target_mips*,
+             Output_section*, size_t relnum,
+             const elfcpp::Rela<size, big_endian>&,
+             unsigned int, const Sized_symbol<size>*,
+             const Symbol_value<size>*,
+             unsigned char*,
+             typename elfcpp::Elf_types<size>::Elf_Addr,
+             section_size_type);
+  };
+
+  // A class which returns the size required for a relocation type,
+  // used while scanning relocs during a relocatable link.
+  class Relocatable_size_for_reloc
+  {
+   public:
+    unsigned int
+    get_size_for_reloc(unsigned int, Relobj*);
+  };
+
+  // This POD class holds the dynamic relocations that should be emitted instead
+  // of R_MIPS_32, R_MIPS_REL32 and R_MIPS_64 relocations.  We will emit these
+  // relocations if it turns out that the symbol does not have static
+  // relocations.
+  class Dyn_reloc
+  {
+   public:
+    Dyn_reloc(Mips_symbol<size>* sym, unsigned int r_type,
+              Sized_relobj_file<size, big_endian>* relobj,
+              unsigned int shndx,
+              Output_section* output_section,
+              typename elfcpp::Elf_types<size>::Elf_Addr r_offset)
+      : sym_(sym), r_type_(r_type), relobj_(relobj),
+        shndx_(shndx), output_section_(output_section),
+        r_offset_(r_offset)
+    { }
+
+    // Emit this reloc if appropriate.  This is called after we have
+    // scanned all the relocations, so we know whether the symbol has
+    // static relocations.
+    void
+    emit(Reloc_section* rel_dyn, Mips_output_data_got<size, big_endian>* got,
+         Symbol_table *symtab)
+    {
+      if (!this->sym_->has_static_relocs())
+        {
+          got->record_global_got_symbol(this->sym_, this->relobj_, this->r_type_,
+                                        true, false);
+          if (!symbol_references_local (this->sym_,
+                                  should_add_dynsym_entry(this->sym_, symtab)))
+            rel_dyn->add_global(this->sym_, this->r_type_,
+                                this->output_section_, this->relobj_,
+                                this->shndx_, this->r_offset_);
+          else
+            rel_dyn->add_symbolless_global_addend(this->sym_, this->r_type_,
+                                          this->output_section_, this->relobj_,
+                                          this->shndx_, this->r_offset_);
+        }
+    }
+
+   private:
+    Mips_symbol<size>* sym_;
+    unsigned int r_type_;
+    Sized_relobj_file<size, big_endian>* relobj_;
+    unsigned int shndx_;
+    Output_section* output_section_;
+    typename elfcpp::Elf_types<size>::Elf_Addr r_offset_;
+  };
+
+  // Adjust TLS relocation type based on the options and whether this
+  // is a local symbol.
+  static tls::Tls_optimization
+  optimize_tls_reloc(bool is_final, int r_type);
+
+  // Return whether there is a GOT section.
+  bool
+  has_got_section() const
+  { return this->got_ != NULL; }
+
+  // Check whether the given ELF header flags describe a 32-bit binary.
+  bool
+  mips_32bit_flags(elfcpp::Elf_Word);
+
+  enum Mips_mach {
+    mach_mips3000             = 3000,
+    mach_mips3900             = 3900,
+    mach_mips4000             = 4000,
+    mach_mips4010             = 4010,
+    mach_mips4100             = 4100,
+    mach_mips4111             = 4111,
+    mach_mips4120             = 4120,
+    mach_mips4300             = 4300,
+    mach_mips4400             = 4400,
+    mach_mips4600             = 4600,
+    mach_mips4650             = 4650,
+    mach_mips5000             = 5000,
+    mach_mips5400             = 5400,
+    mach_mips5500             = 5500,
+    mach_mips6000             = 6000,
+    mach_mips7000             = 7000,
+    mach_mips8000             = 8000,
+    mach_mips9000             = 9000,
+    mach_mips10000            = 10000,
+    mach_mips12000            = 12000,
+    mach_mips14000            = 14000,
+    mach_mips16000            = 16000,
+    mach_mips16               = 16,
+    mach_mips5                = 5,
+    mach_mips_loongson_2e     = 3001,
+    mach_mips_loongson_2f     = 3002,
+    mach_mips_loongson_3a     = 3003,
+    mach_mips_sb1             = 12310201, // octal 'SB', 01
+    mach_mips_octeon          = 6501,
+    mach_mips_octeonp         = 6601,
+    mach_mips_octeon2         = 6502,
+    mach_mips_xlr             = 887682,   // decimal 'XLR'
+    mach_mipsisa32            = 32,
+    mach_mipsisa32r2          = 33,
+    mach_mipsisa64            = 64,
+    mach_mipsisa64r2          = 65,
+    mach_mips_micromips       = 96
+  };
+
+  // Return the MACH for a MIPS e_flags value.
+  unsigned int
+  elf_mips_mach(elfcpp::Elf_Word);
+
+  // Check whether machine EXTENSION is an extension of machine BASE.
+  bool
+  mips_mach_extends(unsigned int, unsigned int);
+
+  // Merge processor specific flags.
+  void
+  merge_processor_specific_flags(const std::string&, elfcpp::Elf_Word,
+                                 unsigned char, bool);
+
+  // True if we are linking for CPUs that are faster if JAL is converted to BAL.
+  static inline bool
+  jal_to_bal()
+  { return false; }
+
+  // True if we are linking for CPUs that are faster if JALR is converted to
+  // BAL.  This should be safe for all architectures.  We enable this predicate
+  // for all CPUs.
+  static inline bool
+  jalr_to_bal()
+  { return true; }
+
+  // True if we are linking for CPUs that are faster if JR is converted to B.
+  // This should be safe for all architectures.  We enable this predicate for
+  // all CPUs.
+  static inline bool
+  jr_to_b()
+  { return true; }
+
+  // Return the size of the GOT section.
+  section_size_type
+  got_size() const
+  {
+    gold_assert(this->got_ != NULL);
+    return this->got_->data_size();
+  }
+
+  // Create a PLT entry for a global symbol.
+  void
+  make_plt_entry(Symbol_table*, Layout*, Symbol*);
+
+  // Create a .MIPS.stubs entry for a global symbol.
+  void
+  make_lazy_stub_entry(Layout*, Symbol*);
+
+  // Get the PLT section.
+  const Mips_output_data_plt<size, big_endian>*
+  plt_section() const
+  {
+    gold_assert(this->plt_ != NULL);
+    return this->plt_;
+  }
+
+  // Get the GOT PLT section.
+  const Mips_output_data_plt<size, big_endian>*
+  got_plt_section() const
+  {
+    gold_assert(this->got_plt_ != NULL);
+    return this->got_plt_;
+  }
+
+  // Copy a relocation against a global symbol.
+  void
+  copy_reloc(Symbol_table* symtab, Layout* layout,
+             Sized_relobj_file<size, big_endian>* object,
+             unsigned int shndx, Output_section* output_section,
+             Symbol* sym, const elfcpp::Rel<size, big_endian>& reloc)
+  {
+    this->copy_relocs_.copy_reloc(symtab, layout,
+                                  symtab->get_sized_symbol<size>(sym),
+                                  object, shndx, output_section,
+                                  reloc, this->rel_dyn_section(layout));
+  }
+
+  // Copy a relocation against a global symbol.
+  void
+  copy_reloc(Symbol_table* symtab, Layout* layout,
+             Sized_relobj_file<size, big_endian>* object,
+             unsigned int shndx, Output_section* output_section,
+             Symbol* sym, const elfcpp::Rela<size, big_endian>& reloc)
+  {
+    this->copy_relocsa_.copy_reloc(symtab, layout,
+                                   symtab->get_sized_symbol<size>(sym),
+                                   object, shndx, output_section,
+                                   reloc, this->rela_dyn_section(layout));
+  }
+
+  void
+  dynamic_reloc(Mips_symbol<size>* sym, unsigned int r_type,
+                Sized_relobj_file<size, big_endian>* relobj,
+                unsigned int shndx,
+                Output_section* output_section,
+                typename elfcpp::Elf_types<size>::Elf_Addr r_offset)
+  {
+    this->dyn_relocs_.push_back(Dyn_reloc(sym, r_type, relobj, shndx,
+                                          output_section, r_offset));
+  }
+
+  // Calculate value of _gp symbol.
+  void
+  set_gp(Layout*, Symbol_table*);
+
+  const char*
+  elf_mips_abi_name(elfcpp::Elf_Word e_flags, unsigned char ei_class);
+  const char*
+  elf_mips_mach_name (elfcpp::Elf_Word e_flags);
+
+  // Adds entries that describe how machines relate to one another.  The entries
+  // are ordered topologically with MIPS I extensions listed last.  First
+  // element is extension, second element is base.
+  void
+  add_machine_extensions()
+  {
+    // MIPS64r2 extensions.
+    this->add_extension(mach_mips_octeon2, mach_mips_octeonp);
+    this->add_extension(mach_mips_octeonp, mach_mips_octeon);
+    this->add_extension(mach_mips_octeon, mach_mipsisa64r2);
+
+    // MIPS64 extensions.
+    this->add_extension(mach_mipsisa64r2, mach_mipsisa64);
+    this->add_extension(mach_mips_sb1, mach_mipsisa64);
+    this->add_extension(mach_mips_xlr, mach_mipsisa64);
+    this->add_extension(mach_mips_loongson_3a, mach_mipsisa64);
+
+    // MIPS V extensions.
+    this->add_extension(mach_mipsisa64, mach_mips5);
+
+    // R10000 extensions.
+    this->add_extension(mach_mips12000, mach_mips10000);
+    this->add_extension(mach_mips14000, mach_mips10000);
+    this->add_extension(mach_mips16000, mach_mips10000);
+
+    // R5000 extensions.  Note: the vr5500 ISA is an extension of the core
+    // vr5400 ISA, but doesn't include the multimedia stuff.  It seems
+    // better to allow vr5400 and vr5500 code to be merged anyway, since
+    // many libraries will just use the core ISA.  Perhaps we could add
+    // some sort of ASE flag if this ever proves a problem.
+    this->add_extension(mach_mips5500, mach_mips5400);
+    this->add_extension(mach_mips5400, mach_mips5000);
+
+    // MIPS IV extensions.
+    this->add_extension(mach_mips5, mach_mips8000);
+    this->add_extension(mach_mips10000, mach_mips8000);
+    this->add_extension(mach_mips5000, mach_mips8000);
+    this->add_extension(mach_mips7000, mach_mips8000);
+    this->add_extension(mach_mips9000, mach_mips8000);
+
+    // VR4100 extensions.
+    this->add_extension(mach_mips4120, mach_mips4100);
+    this->add_extension(mach_mips4111, mach_mips4100);
+
+    // MIPS III extensions.
+    this->add_extension(mach_mips_loongson_2e, mach_mips4000);
+    this->add_extension(mach_mips_loongson_2f, mach_mips4000);
+    this->add_extension(mach_mips8000, mach_mips4000);
+    this->add_extension(mach_mips4650, mach_mips4000);
+    this->add_extension(mach_mips4600, mach_mips4000);
+    this->add_extension(mach_mips4400, mach_mips4000);
+    this->add_extension(mach_mips4300, mach_mips4000);
+    this->add_extension(mach_mips4100, mach_mips4000);
+    this->add_extension(mach_mips4010, mach_mips4000);
+
+    // MIPS32 extensions.
+    this->add_extension(mach_mipsisa32r2, mach_mipsisa32);
+
+    // MIPS II extensions.
+    this->add_extension(mach_mips4000, mach_mips6000);
+    this->add_extension(mach_mipsisa32, mach_mips6000);
+
+    // MIPS I extensions.
+    this->add_extension(mach_mips6000, mach_mips3000);
+    this->add_extension(mach_mips3900, mach_mips3000);
+  }
+
+  // Add value to MIPS extenstions.
+  void
+  add_extension(unsigned int base, unsigned int extension)
+  {
+    std::pair<unsigned int, unsigned int> ext(base, extension);
+    this->mips_mach_extensions_.push_back(ext);
+  }
+
+  // Return mips16 fn stub section for SYM, or NULL if there isn't any.
+  Mips16_stub_section*
+  get_mips16_fn_stub(const Symbol* sym) const
+  {
+    typename Mips16_stubs_map::const_iterator it =
+      this->mips16_fn_stubs_.find(sym);
+    if (it != this->mips16_fn_stubs_.end())
+      return it->second;
+    return NULL;
+  }
+
+  // Set mips16 fn stub section for symbol SYM.
+  bool
+  add_mips16_fn_stub(const Symbol *sym, Mips16_stub_section *stub)
+  {
+    std::pair<typename Mips16_stubs_map::iterator, bool> result =
+      this->mips16_fn_stubs_.insert(
+        std::pair<const Symbol*, Mips16_stub_section*>(sym, stub));
+    return result.second;
+  }
+
+  // Return mips16 call stub section for SYM, or NULL if there isn't any.
+  Mips16_stub_section*
+  get_mips16_call_stub(const Symbol* sym) const
+  {
+    typename Mips16_stubs_map::const_iterator it =
+      this->mips16_call_stubs_.find(sym);
+    if (it != this->mips16_call_stubs_.end())
+      return it->second;
+    return NULL;
+  }
+
+  // Set mips16 call stub section for symbol SYM.
+  bool
+  add_mips16_call_stub(const Symbol *sym, Mips16_stub_section *stub)
+  {
+    std::pair<typename Mips16_stubs_map::iterator, bool> result =
+      this->mips16_call_stubs_.insert(
+        std::pair<const Symbol*, Mips16_stub_section*>(sym, stub));
+    return result.second;
+  }
+
+  // Return mips16 call.fp stub section for SYM, or NULL if there isn't any.
+  Mips16_stub_section*
+  get_mips16_call_fp_stub(const Symbol* sym) const
+  {
+    typename Mips16_stubs_map::const_iterator it =
+      this->mips16_call_fp_stubs_.find(sym);
+    if (it != this->mips16_call_fp_stubs_.end())
+      return it->second;
+    return NULL;
+  }
+
+  // Set mips16 call.fp stub section for symbol SYM.
+  bool
+  add_mips16_call_fp_stub(const Symbol *sym, Mips16_stub_section *stub)
+  {
+    std::pair<typename Mips16_stubs_map::iterator, bool> result =
+      this->mips16_call_fp_stubs_.insert(
+        std::pair<const Symbol*, Mips16_stub_section*>(sym, stub));
+    return result.second;
+  }
+
+  // Information about this specific target which we pass to the
+  // general Target structure.
+  static Target::Target_info mips_info;
+  // The GOT section.
+  Mips_output_data_got<size, big_endian>* got_;
+  // gp symbol
+  Sized_symbol<size>* gp_;
+  // The PLT section.
+  Mips_output_data_plt<size, big_endian>* plt_;
+  // The GOT PLT section.
+  Output_data_space* got_plt_;
+  // The dynamic reloc section.
+  Reloc_section* rel_dyn_;
+  Reloca_section* rela_dyn_;
+  // Relocs saved to avoid a COPY reloc.
+  Mips_copy_relocs<elfcpp::SHT_REL, size, big_endian> copy_relocs_;
+  Mips_copy_relocs<elfcpp::SHT_RELA, size, big_endian> copy_relocsa_;
+
+  // A list of dyn relocs to be saved.
+  std::vector<Dyn_reloc> dyn_relocs_;
+
+  // The LA25 stub section.
+  Mips_output_data_la25_stub<size, big_endian>* la25_stub_;
+  // Architecture extensions.
+  std::vector<std::pair<unsigned int, unsigned int> > mips_mach_extensions_;
+  // .MIPS.stubs
+  Mips_output_data_mips_stubs<size, big_endian>* mips_stubs_;
+
+  unsigned char ei_class_;
+  unsigned int mach_;
+  Layout* layout_;
+
+  typedef Unordered_map<const Symbol*, Mips16_stub_section*> Mips16_stubs_map;
+
+  typename std::list<got16_addend<size, big_endian> > got16_addends;
+
+  // Map of mips16 fn stubs for global 16bit functions. 32 bit functions
+  // should use this stubs to call 16 bit functions.
+  Mips16_stubs_map mips16_fn_stubs_;
+
+  Mips16_stubs_map mips16_call_stubs_;
+  Mips16_stubs_map mips16_call_fp_stubs_;
+};
+
+
+// Helper structure for R_MIPS*_HI16/LO16 and R_MIPS*_GOT16/LO16 relocations. It
+// records high part of the relocation pair.
+
+template<int size, bool big_endian>
+struct reloc_high
+{
+  reloc_high(unsigned char* _view,
+    const Sized_relobj_file<size, big_endian>* _object,
+    const Symbol_value<size>* _psymval,
+    typename elfcpp::Elf_types<size>::Elf_Addr _addend,
+    unsigned int _r_type, unsigned int _rel_type,
+    typename elfcpp::Elf_types<size>::Elf_Addr _address = 0,
+    bool _gp_disp = false)
+    : view(_view), object(_object), psymval(_psymval), addend(_addend),
+      r_type(_r_type), rel_type(_rel_type), address(_address), gp_disp(_gp_disp)
+  { }
+
+  unsigned char* view;
+  const Sized_relobj_file<size, big_endian>* object;
+  const Symbol_value<size>* psymval;
+  typename elfcpp::Elf_types<size>::Elf_Addr addend;
+  unsigned int r_type;
+  unsigned int rel_type;
+  typename elfcpp::Elf_types<size>::Elf_Addr address;
+  bool gp_disp;
+};
+
+template<int size, bool big_endian>
+class Mips_relocate_functions : public Relocate_functions<size, big_endian>
+{
+ public:
+  typedef enum
+  {
+    STATUS_OKAY,        // No error during relocation.
+    STATUS_OVERFLOW,    // Relocation overflow.
+    STATUS_BAD_RELOC    // Relocation cannot be applied.
+  } Status;
+
+ private:
+  typedef Relocate_functions<size, big_endian> Base;
+  typedef Mips_relocate_functions<size, big_endian> This;
+
+  static typename std::list<reloc_high<size, big_endian> > hi16_relocs;
+  static typename std::list<reloc_high<size, big_endian> > got16_relocs;
+
+  //   R_MIPS16_26 is used for the mips16 jal and jalx instructions.
+  //   Most mips16 instructions are 16 bits, but these instructions
+  //   are 32 bits.
+  //
+  //   The format of these instructions is:
+  //
+  //   +--------------+--------------------------------+
+  //   |     JALX     | X|   Imm 20:16  |   Imm 25:21  |
+  //   +--------------+--------------------------------+
+  //   |                Immediate  15:0                |
+  //   +-----------------------------------------------+
+  //
+  //   JALX is the 5-bit value 00011.  X is 0 for jal, 1 for jalx.
+  //   Note that the immediate value in the first word is swapped.
+  //
+  //   When producing a relocatable object file, R_MIPS16_26 is
+  //   handled mostly like R_MIPS_26.  In particular, the addend is
+  //   stored as a straight 26-bit value in a 32-bit instruction.
+  //   (gas makes life simpler for itself by never adjusting a
+  //   R_MIPS16_26 reloc to be against a section, so the addend is
+  //   always zero).  However, the 32 bit instruction is stored as 2
+  //   16-bit values, rather than a single 32-bit value.  In a
+  //   big-endian file, the result is the same; in a little-endian
+  //   file, the two 16-bit halves of the 32 bit value are swapped.
+  //   This is so that a disassembler can recognize the jal
+  //   instruction.
+  //
+  //   When doing a final link, R_MIPS16_26 is treated as a 32 bit
+  //   instruction stored as two 16-bit values.  The addend A is the
+  //   contents of the targ26 field.  The calculation is the same as
+  //   R_MIPS_26.  When storing the calculated value, reorder the
+  //   immediate value as shown above, and don't forget to store the
+  //   value as two 16-bit values.
+  //
+  //   To put it in MIPS ABI terms, the relocation field is T-targ26-16,
+  //   defined as
+  //
+  //   big-endian:
+  //   +--------+----------------------+
+  //   |        |                      |
+  //   |        |    targ26-16         |
+  //   |31    26|25                   0|
+  //   +--------+----------------------+
+  //
+  //   little-endian:
+  //   +----------+------+-------------+
+  //   |          |      |             |
+  //   |  sub1    |      |     sub2    |
+  //   |0        9|10  15|16         31|
+  //   +----------+--------------------+
+  //   where targ26-16 is sub1 followed by sub2 (i.e., the addend field A is
+  //   ((sub1 << 16) | sub2)).
+  //
+  //   When producing a relocatable object file, the calculation is
+  //   (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
+  //   When producing a fully linked file, the calculation is
+  //   let R = (((A < 2) | ((P + 4) & 0xf0000000) + S) >> 2)
+  //   ((R & 0x1f0000) << 5) | ((R & 0x3e00000) >> 5) | (R & 0xffff)
+  //
+  //   The table below lists the other MIPS16 instruction relocations.
+  //   Each one is calculated in the same way as the non-MIPS16 relocation
+  //   given on the right, but using the extended MIPS16 layout of 16-bit
+  //   immediate fields:
+  //
+  //      R_MIPS16_GPREL          R_MIPS_GPREL16
+  //      R_MIPS16_GOT16          R_MIPS_GOT16
+  //      R_MIPS16_CALL16         R_MIPS_CALL16
+  //      R_MIPS16_HI16           R_MIPS_HI16
+  //      R_MIPS16_LO16           R_MIPS_LO16
+  //
+  //   A typical instruction will have a format like this:
+  //
+  //   +--------------+--------------------------------+
+  //   |    EXTEND    |     Imm 10:5    |   Imm 15:11  |
+  //   +--------------+--------------------------------+
+  //   |    Major     |   rx   |   ry   |   Imm  4:0   |
+  //   +--------------+--------------------------------+
+  //
+  //   EXTEND is the five bit value 11110.  Major is the instruction
+  //   opcode.
+  //
+  //   All we need to do here is shuffle the bits appropriately.
+  //   As above, the two 16-bit halves must be swapped on a
+  //   little-endian system.
+
+  // Similar to MIPS16, the two 16-bit halves in microMIPS must be swapped
+  // on a little-endian system.  This does not apply to R_MICROMIPS_PC7_S1
+  // and R_MICROMIPS_PC10_S1 relocs that apply to 16-bit instructions.
+
+  static inline bool
+  micromips_reloc_shuffle(unsigned int r_type)
+  {
+    return (Target_mips<size, big_endian>::micromips_reloc(r_type)
+            && r_type != elfcpp::R_MICROMIPS_PC7_S1
+            && r_type != elfcpp::R_MICROMIPS_PC10_S1);
+  }
+
+  static void
+  mips_reloc_unshuffle(unsigned char* view, unsigned int r_type, bool jal_shuffle)
+  {
+    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype16;
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype32;
+
+    if (!Target_mips<size, big_endian>::mips16_reloc(r_type)
+        && !micromips_reloc_shuffle(r_type))
+      return;
+
+    // Pick up the first and second halfwords of the instruction.
+    Valtype16 first = elfcpp::Swap<16, big_endian>::readval(view);
+    Valtype16 second = elfcpp::Swap<16, big_endian>::readval(view + 2);
+    Valtype32 val;
+
+    if (Target_mips<size, big_endian>::micromips_reloc(r_type)
+        || (r_type == elfcpp::R_MIPS16_26 && !jal_shuffle))
+      val = first << 16 | second;
+    else if (r_type != elfcpp::R_MIPS16_26)
+      val = (((first & 0xf800) << 16) | ((second & 0xffe0) << 11)
+             | ((first & 0x1f) << 11) | (first & 0x7e0) | (second & 0x1f));
+    else
+      val = (((first & 0xfc00) << 16) | ((first & 0x3e0) << 11)
+             | ((first & 0x1f) << 21) | second);
+
+    elfcpp::Swap<32, big_endian>::writeval(view, val);
+  }
+
+  static void
+  mips_reloc_shuffle(unsigned char* view, unsigned int r_type, bool jal_shuffle)
+  {
+    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype16;
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype32;
+
+    if (!Target_mips<size, big_endian>::mips16_reloc(r_type)
+        && !micromips_reloc_shuffle(r_type))
+      return;
+
+    Valtype32 val = elfcpp::Swap<32, big_endian>::readval(view);
+    Valtype16 first, second;
+
+    if (Target_mips<size, big_endian>::micromips_reloc(r_type)
+        || (r_type == elfcpp::R_MIPS16_26 && !jal_shuffle))
+      {
+        second = val & 0xffff;
+        first = val >> 16;
+      }
+    else if (r_type != elfcpp::R_MIPS16_26)
+      {
+        second = ((val >> 11) & 0xffe0) | (val & 0x1f);
+        first = ((val >> 16) & 0xf800) | ((val >> 11) & 0x1f) | (val & 0x7e0);
+      }
+    else
+      {
+        second = val & 0xffff;
+        first = ((val >> 16) & 0xfc00) | ((val >> 11) & 0x3e0)
+                 | ((val >> 21) & 0x1f);
+      }
+
+    elfcpp::Swap<16, big_endian>::writeval(view + 2, second);
+    elfcpp::Swap<16, big_endian>::writeval(view, first);
+  }
+
+ public:
+  // R_MIPS_16: S + sign-extend(A)
+  static inline typename This::Status
+  rel16(unsigned char* view,
+        const Sized_relobj_file<size, big_endian>* object,
+        const Symbol_value<size>* psymval,
+        typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+        unsigned int rel_type)
+  {
+    typedef typename elfcpp::Swap<16, big_endian>::Valtype Valtype;
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Reltype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<16, big_endian>::readval(wv);
+    Reltype addend;
+
+    if (rel_type == elfcpp::SHT_RELA)
+      addend = Bits<16>::sign_extend32(addend_o);
+    else
+      addend = Bits<16>::sign_extend32(val);
+
+    Reltype x = psymval->value(object, addend);
+    val = Bits<16>::bit_select32(val, x, 0xffffU);
+    elfcpp::Swap<16, big_endian>::writeval(wv, val);
+    return (Bits<16>::has_overflow32(x)
+            ? This::STATUS_OVERFLOW
+            : This::STATUS_OKAY);
+  }
+
+  // R_MIPS_32: S + A
+  static inline typename This::Status
+  rel32(unsigned char* view,
+        const Sized_relobj_file<size, big_endian>* object,
+        const Symbol_value<size>* psymval,
+        typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+        unsigned int rel_type)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype addend;
+
+    if (rel_type == elfcpp::SHT_RELA)
+      addend = Bits<32>::sign_extend32(addend_o);
+    else
+      addend = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    Valtype x = psymval->value(object, addend);
+    elfcpp::Swap<32, big_endian>::writeval(wv, x);
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_JALR, R_MICROMIPS_JALR
+  static inline typename This::Status
+  reljalr(unsigned char* view,
+          const Sized_relobj_file<size, big_endian>* object,
+          const Symbol_value<size>* psymval,
+          typename elfcpp::Elf_types<size>::Elf_Addr address,
+          bool cross_mode_jump, unsigned int r_type,
+          bool jalr_to_bal, bool jr_to_b)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    mips_reloc_unshuffle(view, r_type, false);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    // Try converting J(AL)R to B(AL), if the target is in range.
+    if (!parameters->options().relocatable()
+        && r_type == elfcpp::R_MIPS_JALR
+        && !cross_mode_jump
+        && ((jalr_to_bal && val == 0x0320f809)    // jalr t9
+            || (jr_to_b && val == 0x03200008)))   // jr t9
+      {
+        int offset = psymval->value(object, 0) - (address + 4);
+        if (!Bits<18>::has_overflow32(offset))
+          {
+            if (val == 0x03200008)   // jr t9
+              val = 0x10000000 | (((Valtype)offset >> 2) & 0xffff);  // b addr
+            else
+              val = 0x04110000 | (((Valtype)offset >> 2) & 0xffff);  // bal addr
+          }
+      }
+
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    mips_reloc_shuffle(view, r_type, false);
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_PC32: S + A - P
+  static inline typename This::Status
+  relpc32(unsigned char* view,
+          const Sized_relobj_file<size, big_endian>* object,
+          const Symbol_value<size>* psymval,
+          typename elfcpp::Elf_types<size>::Elf_Addr address,
+          typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+          unsigned int rel_type)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype addend;
+    if (rel_type == elfcpp::SHT_RELA)
+      addend = Bits<32>::sign_extend32(addend_o);
+    else
+      addend = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    Valtype x = psymval->value(object, addend) - address;
+    elfcpp::Swap<32, big_endian>::writeval(wv, x);
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_26, R_MIPS16_26, R_MICROMIPS_26_S1
+  static inline typename This::Status
+  rel26(unsigned char* view,
+        const Sized_relobj_file<size, big_endian>* object,
+        const Symbol_value<size>* psymval,
+        typename elfcpp::Elf_types<size>::Elf_Addr address,
+        bool local,
+        typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+        unsigned int rel_type, const Symbol* gsym,
+        bool cross_mode_jump, unsigned int r_type, bool jal_to_bal)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+
+    mips_reloc_unshuffle(view, r_type, false);
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    Valtype addend;
+    if (rel_type == elfcpp::SHT_REL)
+      {
+        if (r_type == elfcpp::R_MICROMIPS_26_S1)
+          addend = (val & 0x03ffffff) << 1;
+        else
+          addend = (val & 0x03ffffff) << 2;
+      }
+    else
+      addend = addend_o;
+
+    // Make sure the target of JALX is word-aligned.  Bit 0 must be
+    // the correct ISA mode selector and bit 1 must be 0.
+    if (cross_mode_jump
+        && (psymval->value(object, 0) & 3) != (r_type == elfcpp::R_MIPS_26))
+      {
+        gold_warning(_("JALX to a non-word-aligned address"));
+        return This::STATUS_BAD_RELOC;
+      }
+
+    // Shift is 2, unusually, for microMIPS JALX.
+    unsigned int shift =
+        (!cross_mode_jump && r_type == elfcpp::R_MICROMIPS_26_S1) ? 1 : 2;
+
+    Valtype x;
+    if (local)
+      x = addend | ((address + 4) & (0xfc000000 << shift));
+    else
+      {
+        if (shift == 1)
+          x = Bits<27>::sign_extend32(addend);
+        else
+          x = Bits<28>::sign_extend32(addend);
+      }
+    x = psymval->value(object, x) >> shift;
+
+    if (!local && !gsym->is_weak_undefined())
+      {
+        if ((x >> 26) != ((address + 4) >> (26 + shift)))
+          {
+            gold_error(_("relocation truncated to fit: %u against '%s'"),
+                       r_type, gsym->name());
+            return This::STATUS_OVERFLOW;
+          }
+      }
+
+    val = Bits<32>::bit_select32(val, x, 0x03ffffff);
+
+    // If required, turn JAL into JALX.
+    if (cross_mode_jump)
+      {
+        bool ok;
+        Valtype opcode = val >> 26;
+        Valtype jalx_opcode;
+
+        // Check to see if the opcode is already JAL or JALX.
+        if (r_type == elfcpp::R_MIPS16_26)
+          {
+            ok = (opcode == 0x6) || (opcode == 0x7);
+            jalx_opcode = 0x7;
+          }
+        else if (r_type == elfcpp::R_MICROMIPS_26_S1)
+          {
+            ok = (opcode == 0x3d) || (opcode == 0x3c);
+            jalx_opcode = 0x3c;
+          }
+        else
+          {
+            ok = (opcode == 0x3) || (opcode == 0x1d);
+            jalx_opcode = 0x1d;
+          }
+
+        // If the opcode is not JAL or JALX, there's a problem.  We cannot
+        // convert J or JALS to JALX.
+        if (!ok)
+          {
+            gold_error(_("Unsupported jump between ISA modes; consider "
+                         "recompiling with interlinking enabled."));
+            return This::STATUS_BAD_RELOC;
+          }
+
+        // Make this the JALX opcode.
+        val = (val & ~(0x3f << 26)) | (jalx_opcode << 26);
+      }
+
+    // Try converting JAL to BAL, if the target is in range.
+    if (!parameters->options().relocatable()
+        && !cross_mode_jump
+        && ((jal_to_bal
+            && r_type == elfcpp::R_MIPS_26
+            && (val >> 26) == 0x3)))    // jal addr
+      {
+        Valtype dest = (x << 2) | (((address + 4) >> 28) << 28);
+        int offset = dest - (address + 4);
+        if (!Bits<18>::has_overflow32(offset))
+          {
+            if (val == 0x03200008)   // jr t9
+              val = 0x10000000 | (((Valtype)offset >> 2) & 0xffff);  // b addr
+            else
+              val = 0x04110000 | (((Valtype)offset >> 2) & 0xffff);  // bal addr
+          }
+      }
+
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    mips_reloc_shuffle(view, r_type, !parameters->options().relocatable());
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_PC16
+  static inline typename This::Status
+  relpc16(unsigned char* view,
+          const Sized_relobj_file<size, big_endian>* object,
+          const Symbol_value<size>* psymval,
+          typename elfcpp::Elf_types<size>::Elf_Addr address,
+          typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+          unsigned int rel_type)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    Valtype addend;
+    if (rel_type == elfcpp::SHT_REL)
+      addend = (val & 0xffff) << 2;
+    else
+      addend = addend_o;
+    addend = Bits<18>::sign_extend32(addend);
+
+    Valtype x = psymval->value(object, addend) - address;
+    val = Bits<16>::bit_select32(val, x >> 2, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    return (Bits<18>::has_overflow32(x)
+            ? This::STATUS_OVERFLOW
+            : This::STATUS_OKAY);
+  }
+
+  // R_MIPS_HI16, R_MIPS16_HI16, R_MICROMIPS_HI16,
+  static inline typename This::Status
+  relhi16(unsigned char* view,
+          const Sized_relobj_file<size, big_endian>* object,
+          const Symbol_value<size>* psymval,
+          typename elfcpp::Elf_types<size>::Elf_Addr addend,
+          typename elfcpp::Elf_types<size>::Elf_Addr address,
+          bool gp_disp, unsigned int r_type, unsigned int rel_type)
+  {
+    // Record the relocation. It will be resolved when we find lo16 part.
+    hi16_relocs.push_back(reloc_high<size, big_endian>(view, object, psymval,
+                          addend, r_type, rel_type, address, gp_disp));
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_HI16, R_MIPS16_HI16, R_MICROMIPS_HI16,
+  static inline typename This::Status
+  do_relhi16(unsigned char* view,
+             const Sized_relobj_file<size, big_endian>* object,
+             const Symbol_value<size>* psymval,
+             typename elfcpp::Elf_types<size>::Elf_Addr addend_hi,
+             typename elfcpp::Elf_types<size>::Elf_Addr address,
+             bool gp_disp, unsigned int r_type,
+             unsigned int rel_type,
+             typename elfcpp::Swap<32, big_endian>::Valtype addend_lo,
+             Target_mips<size, big_endian> *target)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    mips_reloc_unshuffle(view, r_type, false);
+    Valtype *wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    Valtype addend;
+    if (rel_type == elfcpp::SHT_REL)
+      addend = ((val & 0xffff) << 16) + addend_lo;
+    else
+      addend = addend_hi;
+
+    Valtype value;
+    if (!gp_disp)
+      value = psymval->value(object, addend);
+    else
+      {
+        // For MIPS16 ABI code we generate this sequence
+        //    0: li      $v0,%hi(_gp_disp)
+        //    4: addiupc $v1,%lo(_gp_disp)
+        //    8: sll     $v0,16
+        //   12: addu    $v0,$v1
+        //   14: move    $gp,$v0
+        // So the offsets of hi and lo relocs are the same, but the
+        // base $pc is that used by the ADDIUPC instruction at $t9 + 4.
+        // ADDIUPC clears the low two bits of the instruction address,
+        // so the base is ($t9 + 4) & ~3.
+        Valtype gp_offset;
+        if (r_type == elfcpp::R_MIPS16_HI16)
+          gp_offset = (target->adjusted_gp_value(object)
+                       - ((address + 4) & ~0x3));
+        // The microMIPS .cpload sequence uses the same assembly
+        // instructions as the traditional psABI version, but the
+        // incoming $t9 has the low bit set.
+        else if (r_type == elfcpp::R_MICROMIPS_HI16)
+          gp_offset = target->adjusted_gp_value(object) - address - 1;
+        else
+          gp_offset = target->adjusted_gp_value(object) - address;
+        value = gp_offset + addend;
+      }
+    Valtype x = ((value + 0x8000) >> 16) & 0xffff;
+    val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    mips_reloc_shuffle(view, r_type, false);
+    return (gp_disp && Bits<16>::has_overflow32(x)
+            ? This::STATUS_OVERFLOW
+            : This::STATUS_OKAY);
+  }
+
+  // R_MIPS_GOT16, R_MIPS16_GOT16, R_MICROMIPS_GOT16
+  static inline typename This::Status
+  relgot16_local(unsigned char* view,
+                 const Sized_relobj_file<size, big_endian>* object,
+                 const Symbol_value<size>* psymval,
+                 typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+                 unsigned int rel_type, unsigned int r_type)
+  {
+    // Record the relocation. It will be resolved when we find lo16 part.
+    got16_relocs.push_back(reloc_high<size, big_endian>(view, object, psymval,
+                           addend_o, r_type, rel_type));
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_GOT16, R_MIPS16_GOT16, R_MICROMIPS_GOT16
+  static inline typename This::Status
+  do_relgot16_local(unsigned char* view,
+                    const Sized_relobj_file<size, big_endian>* object,
+                    const Symbol_value<size>* psymval,
+                    typename elfcpp::Elf_types<size>::Elf_Addr addend_hi,
+                    unsigned int r_type, unsigned int rel_type,
+                    typename elfcpp::Swap<32, big_endian>::Valtype addend_lo,
+                    Target_mips<size, big_endian> *target)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    mips_reloc_unshuffle(view, r_type, false);
+    Valtype *wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    Valtype addend;
+    if (rel_type == elfcpp::SHT_REL)
+      addend = ((val & 0xffff) << 16) + addend_lo;
+    else
+      addend = addend_hi;
+
+    // Find got page entry.
+    Valtype value = ((psymval->value(object, addend) + 0x8000) >> 16) & 0xffff;
+    value <<= 16;
+    unsigned int got_offset =
+      target->got_section()->get_got_page_offset(value, object);
+
+    // Resolve the relocation.
+    Valtype x = (target->got_section()->address() + got_offset
+                 - target->adjusted_gp_value(object));
+    val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    mips_reloc_shuffle(view, r_type, false);
+    return (Bits<16>::has_overflow32(x)
+            ? This::STATUS_OVERFLOW
+            : This::STATUS_OKAY);
+  }
+
+  // R_MIPS_LO16, R_MIPS16_LO16, R_MICROMIPS_LO16, R_MICROMIPS_HI0_LO16
+  static inline typename This::Status
+  rello16(Target_mips<size, big_endian> *target, unsigned char* view,
+          const Sized_relobj_file<size, big_endian>* object,
+          const Symbol_value<size>* psymval,
+          typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+          unsigned int rel_type,
+          typename elfcpp::Elf_types<size>::Elf_Addr address,
+          bool gp_disp, unsigned int r_type)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    mips_reloc_unshuffle(view, r_type, false);
+    Valtype *wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    Valtype addend;
+    if (rel_type == elfcpp::SHT_REL)
+      addend = Bits<16>::sign_extend32(val & 0xffff);
+    else
+      addend = addend_o;
+
+    // Resolve pending R_MIPS_HI16 relocations.
+    typename std::list<reloc_high<size, big_endian> >::iterator it =
+      hi16_relocs.begin();
+    while (it != hi16_relocs.end())
+      {
+        reloc_high<size, big_endian> hi16 = *it;
+        if (hi16.psymval->value(hi16.object, 0) == psymval->value(object, 0))
+          {
+            if (do_relhi16(hi16.view, hi16.object, hi16.psymval, hi16.addend,
+                           hi16.address, hi16.gp_disp, hi16.r_type,
+                           hi16.rel_type, addend, target)
+                == This::STATUS_OVERFLOW)
+              return This::STATUS_OVERFLOW;
+            it = hi16_relocs.erase(it);
+          }
+        else
+          ++it;
+      }
+
+    // Resolve pending local R_MIPS_GOT16 relocations.
+    typename std::list<reloc_high<size, big_endian> >::iterator it2 =
+      got16_relocs.begin();
+    while (it2 != got16_relocs.end())
+      {
+        reloc_high<size, big_endian> got16 = *it2;
+        if (got16.psymval->value(got16.object, 0) == psymval->value(object, 0))
+          {
+            if (do_relgot16_local(got16.view, got16.object, got16.psymval,
+                                  got16.addend, got16.r_type, got16.rel_type,
+                                  addend, target) == This::STATUS_OVERFLOW)
+              return This::STATUS_OVERFLOW;
+            it2 = got16_relocs.erase(it2);
+          }
+        else
+          ++it2;
+      }
+
+    // Resolve R_MIPS_LO16 relocation.
+    Valtype x;
+    if (!gp_disp)
+      x = psymval->value(object, addend);
+    else
+      {
+        // See the comment for R_MIPS16_HI16 above for the reason
+        // for this conditional.
+        Valtype gp_offset;
+        if (r_type == elfcpp::R_MIPS16_LO16)
+          gp_offset = target->adjusted_gp_value(object) - (address & ~0x3);
+        else if (r_type == elfcpp::R_MICROMIPS_LO16
+                 || r_type == elfcpp::R_MICROMIPS_HI0_LO16)
+          gp_offset = target->adjusted_gp_value(object) - address + 3;
+        else
+          gp_offset = target->adjusted_gp_value(object) - address + 4;
+        // The MIPS ABI requires checking the R_MIPS_LO16 relocation
+        // for overflow.  Relocations against _gp_disp are normally
+        // generated from the .cpload pseudo-op.  It generates code
+        // that normally looks like this:
+
+        //   lui    $gp,%hi(_gp_disp)
+        //   addiu  $gp,$gp,%lo(_gp_disp)
+        //   addu   $gp,$gp,$t9
+
+        // Here $t9 holds the address of the function being called,
+        // as required by the MIPS ELF ABI.  The R_MIPS_LO16
+        // relocation can easily overflow in this situation, but the
+        // R_MIPS_HI16 relocation will handle the overflow.
+        // Therefore, we consider this a bug in the MIPS ABI, and do
+        // not check for overflow here.
+        x = gp_offset + addend;
+      }
+    val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    mips_reloc_shuffle(view, r_type, false);
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_CALL16, R_MIPS16_CALL16, R_MICROMIPS_CALL16
+  // R_MIPS_GOT16, R_MIPS16_GOT16, R_MICROMIPS_GOT16
+  // R_MIPS_TLS_GD, R_MIPS16_TLS_GD, R_MICROMIPS_TLS_GD
+  // R_MIPS_TLS_GOTTPREL, R_MIPS16_TLS_GOTTPREL, R_MICROMIPS_TLS_GOTTPREL
+  // R_MIPS_TLS_LDM, R_MIPS16_TLS_LDM, R_MICROMIPS_TLS_LDM
+  // R_MIPS_GOT_DISP, R_MICROMIPS_GOT_DISP
+  static inline typename This::Status
+  relgot(Target_mips<size, big_endian> *target, unsigned char* view,
+         const Sized_relobj_file<size, big_endian>* object,
+         unsigned int got_offset, unsigned int r_type)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    mips_reloc_unshuffle(view, r_type, false);
+    Valtype *wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+    Valtype x = (target->got_section()->address() + got_offset
+                 - target->adjusted_gp_value(object));
+    val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    mips_reloc_shuffle(view, r_type, false);
+    return (Bits<16>::has_overflow32(x)
+            ? This::STATUS_OVERFLOW
+            : This::STATUS_OKAY);
+  }
+
+  // R_MIPS_GOT_PAGE, R_MICROMIPS_GOT_PAGE
+  static inline typename This::Status
+  relgotpage(Target_mips<size, big_endian> *target, unsigned char* view,
+             const Sized_relobj_file<size, big_endian>* object,
+             const Symbol_value<size>* psymval,
+             typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+             unsigned int rel_type)
+  {
+    // Find a GOT page entry that points to within 32KB of symbol + addend.
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype *wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(view);
+    Valtype addend = rel_type == elfcpp::SHT_REL ? val & 0xffff : addend_o;
+
+    // Find got page entry.
+    typename elfcpp::Elf_types<size>::Elf_Addr value;
+    value = (psymval->value(object, addend) + 0x8000) & ~0xffff;
+    unsigned int  got_offset =
+      target->got_section()->get_got_page_offset(value, object);
+
+    Valtype x = (target->got_section()->address() + got_offset
+                 - target->adjusted_gp_value(object));
+    val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    return (Bits<16>::has_overflow32(x)
+            ? This::STATUS_OVERFLOW
+            : This::STATUS_OKAY);
+  }
+
+  // R_MIPS_GOT_OFST, R_MICROMIPS_GOT_OFST
+  static inline typename This::Status
+  relgotofst(Target_mips<size, big_endian> *target, unsigned char* view,
+             const Sized_relobj_file<size, big_endian>* object,
+             const Symbol_value<size>* psymval,
+             typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+             unsigned int rel_type, bool local)
+  {
+    // For a local symbol, find a GOT page entry that points to within 32KB of
+    // symbol + addend. Relocation value is the offset of the got page entry's
+    // value from symbol + addend.
+    // For a global symbol, relocation value is addend.
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype *wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(view);
+    Valtype addend = rel_type == elfcpp::SHT_REL ? val & 0xffff : addend_o;
+    Valtype x;
+    if (local)
+      {
+        // Find got page entry.
+        typename elfcpp::Elf_types<size>::Elf_Addr value;
+        value = (psymval->value(object, addend) + 0x8000) & ~0xffff;
+        target->got_section()->get_got_page_offset(value, object);
+
+        x = psymval->value(object, addend) - value;
+      }
+    else
+      x = addend;
+    val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    return (Bits<16>::has_overflow32(x)
+            ? This::STATUS_OVERFLOW
+            : This::STATUS_OKAY);
+  }
+
+  // R_MIPS_GOT_HI16, R_MIPS_CALL_HI16,
+  // R_MICROMIPS_GOT_HI16, R_MICROMIPS_CALL_HI16
+  static inline typename This::Status
+  relgot_hi16(Target_mips<size, big_endian> *target, unsigned char* view,
+              const Sized_relobj_file<size, big_endian>* object,
+              unsigned int got_offset)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype *wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+    Valtype x = (target->got_section()->address() + got_offset
+                 - target->adjusted_gp_value(object));
+    x = ((x + 0x8000) >> 16) & 0xffff;
+    val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_GOT_LO16, R_MIPS_CALL_LO16,
+  // R_MICROMIPS_GOT_LO16, R_MICROMIPS_CALL_LO16
+  static inline typename This::Status
+  relgot_lo16(Target_mips<size, big_endian> *target, unsigned char* view,
+              const Sized_relobj_file<size, big_endian>* object,
+              unsigned int got_offset)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype *wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+    Valtype x = (target->got_section()->address() + got_offset
+                 - target->adjusted_gp_value(object));
+    val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_GPREL16, R_MIPS16_GPREL, R_MIPS_LITERAL, R_MICROMIPS_LITERAL
+  // R_MICROMIPS_GPREL7_S2, R_MICROMIPS_GPREL16
+  static inline typename This::Status
+  relgprel(unsigned char* view,
+           const Sized_relobj_file<size, big_endian>* object,
+           const Symbol_value<size>* psymval,
+           const typename elfcpp::Swap<size, big_endian>::Valtype gp,
+           typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+           unsigned int rel_type,
+           bool local, unsigned int r_type)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+
+    const Mips_relobj<size, big_endian>* mips_relobj =
+      Mips_relobj<size, big_endian>::as_mips_relobj(object);
+
+    mips_reloc_unshuffle(view, r_type, false);
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+
+    Valtype addend;
+    if (rel_type == elfcpp::SHT_RELA)
+      addend = addend_o;
+    else
+      {
+        if (r_type == elfcpp::R_MICROMIPS_GPREL7_S2)
+          addend = (val & 0x7f) << 2;
+        else
+          addend = val & 0xffff;
+        // Only sign-extend the addend if it was extracted from the
+        // instruction.  If the addend was separate, leave it alone,
+        // otherwise we may lose significant bits.
+        addend = Bits<16>::sign_extend32(addend);
+      }
+
+    Valtype x = psymval->value(object, addend) - gp;
+
+    // If the symbol was local, any earlier relocatable links will
+    // have adjusted its addend with the gp offset, so compensate
+    // for that now.  Don't do it for symbols forced local in this
+    // link, though, since they won't have had the gp offset applied
+    // to them before.
+    if (local)
+      x += mips_relobj->gp_value();
+
+    if (r_type == elfcpp::R_MICROMIPS_GPREL7_S2)
+      val = Bits<32>::bit_select32(val, x, 0x7f);
+    else
+      val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    mips_reloc_shuffle(view, r_type, false);
+    if (Bits<16>::has_overflow32(x))
+      {
+        gold_error(_("small-data section exceeds 64KB; lower small-data size "
+                     "limit (see option -G)"));
+        return This::STATUS_OVERFLOW;
+      }
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_GPREL32
+  static inline typename This::Status
+  relgprel32(unsigned char* view,
+             const Sized_relobj_file<size, big_endian>* object,
+             const Symbol_value<size>* psymval,
+             const typename elfcpp::Swap<size, big_endian>::Valtype gp,
+             typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+             unsigned int rel_type)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+
+    const Mips_relobj<size, big_endian>* mips_relobj =
+      Mips_relobj<size, big_endian>::as_mips_relobj(object);
+
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+    Valtype addend = (rel_type == elfcpp::SHT_REL) ? val : addend_o;
+
+    // R_MIPS_GPREL32 relocations are defined for local symbols only.
+    Valtype x = psymval->value(object, addend) + mips_relobj->gp_value() - gp;
+    elfcpp::Swap<32, big_endian>::writeval(wv, x);
+    return This::STATUS_OKAY;
+ }
+
+  // R_MIPS_TLS_TPREL_HI16, R_MIPS16_TLS_TPREL_HI16, R_MICROMIPS_TLS_TPREL_HI16
+  // R_MIPS_TLS_DTPREL_HI16, R_MIPS16_TLS_DTPREL_HI16,
+  // R_MICROMIPS_TLS_DTPREL_HI16
+  static inline typename This::Status
+  tlsrelhi16(unsigned char* view,
+             const Sized_relobj_file<size, big_endian>* object,
+             const Symbol_value<size>* psymval,
+             const typename elfcpp::Swap<32, big_endian>::Valtype tp_offset,
+             typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+             unsigned int rel_type)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype* wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+    Valtype addend = rel_type == elfcpp::SHT_REL ? val & 0xffff : addend_o;
+
+    // tls symbol values are relative to tls_segment()->vaddr()
+    Valtype x = ((psymval->value(object, addend) - tp_offset) + 0x8000) >> 16;
+    val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    return This::STATUS_OKAY;
+  }
+
+  // R_MIPS_TLS_TPREL_LO16, R_MIPS16_TLS_TPREL_LO16, R_MICROMIPS_TLS_TPREL_LO16,
+  // R_MIPS_TLS_DTPREL_LO16, R_MIPS16_TLS_DTPREL_LO16,
+  // R_MICROMIPS_TLS_DTPREL_LO16,
+  // R_MIPS_TLS_TPREL32, R_MIPS_TLS_TPREL64,
+  // R_MIPS_TLS_DTPREL32, R_MIPS_TLS_DTPREL64
+  static inline typename This::Status
+  tlsrello16(unsigned char* view,
+             const Sized_relobj_file<size, big_endian>* object,
+             const Symbol_value<size>* psymval,
+             const typename elfcpp::Swap<32, big_endian>::Valtype tp_offset,
+             typename elfcpp::Elf_types<size>::Elf_Addr addend_o,
+             unsigned int rel_type)
+  {
+    typedef typename elfcpp::Swap<32, big_endian>::Valtype Valtype;
+    Valtype *wv = reinterpret_cast<Valtype*>(view);
+    Valtype val = elfcpp::Swap<32, big_endian>::readval(wv);
+    Valtype addend = rel_type == elfcpp::SHT_REL ? val & 0xffff : addend_o;
+
+    // tls symbol values are relative to tls_segment()->vaddr()
+    Valtype x = psymval->value(object, addend) - tp_offset;
+    val = Bits<32>::bit_select32(val, x, 0xffff);
+    elfcpp::Swap<32, big_endian>::writeval(wv, val);
+    return This::STATUS_OKAY;
+  }
+};
+
+template<int size, bool big_endian>
+typename std::list<reloc_high<size, big_endian> >
+    Mips_relocate_functions<size, big_endian>::hi16_relocs;
+
+template<int size, bool big_endian>
+typename std::list<reloc_high<size, big_endian> >
+    Mips_relocate_functions<size, big_endian>::got16_relocs;
+

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]