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]

RE: Mips target in gold - revision 3 - part 2


Attached is the second part of the updated patch.

Regards,
Sasa
________________________________________
From: Cary Coutant [ccoutant@google.com]
Sent: Tuesday, April 15, 2014 10:46 PM
To: Sasa Stankovic
Cc: Rich Fuhler; binutils@sourceware.org; iant@google.com; Petar Jovanovic
Subject: Re: Mips target in gold - revision 3 - part 2

>> If I understand the code correctly, in this case the HI16 reloc
>> will remain on the got16_addends_ list forever. Is there a point
>> where you know it's safe to remove it? Can you clear the list
>> at the end of each section?
>
> If LO16 is removed by GCC, than HI16 reloc is paired with the next found LO16
> reloc (this means that two or more HI16 reloc will be paired with a single LO16 reloc).
> This is what GNU ld does - it always tries to find LO16 reloc for HI16 reloc, and if it
> can't find it, error is reported.
>
> When trying to find LO16 reloc, GNU ld examines next relocations in the section until
> it finds it, or reports an error.  I implemented it differently in Gold - when HI16 or GOT16
> reloc is found, it is recorded in the got16_addends_ list, and the normal scaning of
> relocations continues.  Then when the LO16 reloc is found, the got16_addends_ list is
> examined and all pending matching HI16 and GOT16 relocs are removed.  The error when
> HI16 or GOT16 reloc doesn't have the LO16 part is detected by checking whether the
> section of the pending HI16 or GOT16 relocation is different from the section of the current
> LO16 relocation. The check whether the final section that is scanned has HI16 or GOT16
> without the LO16 part is in the do_finalize_sections method - I just check whether the
> got16_addends_ list is empty - if it isn't, error is reported.

Ah, I see.

>> >   typename std::list<got16_addend<size, big_endian> > got16_addends_;
>>
>> Do you realy want to use a linked list here? Wouldn't a
>> vector be better?
>
> I used std::list because erase is called on it.

Sorry, I missed that. And now I understand that you don't expect the
list to ever grow very long.

>   // Check whether the final section that was scaned has HI16 or GOT16

Typo: "scanned".

>   // Fill the unused space with zeroes.
>   // TODO(sasa): Can we strip unused bytes during the relaxation?
>   unsigned char* end = oview + oview_size;
>   while (pov < end)
>     {
>       elfcpp::Swap<32, big_endian>::writeval(pov, 0);
>       pov += 4;
>     }

You can just use memset here:

  if (unused > 0)
    memset(pov, 0, unused);

If that's OK with you, I'll commit it with that change.

Thanks for working on this, and for putting up with the long review process!

-cary
diff --git a/gold/mips.cc b/gold/mips.cc
index b0fcc93..bb7a796 100644
--- a/gold/mips.cc
+++ b/gold/mips.cc
@@ -5252,3 +5252,5268 @@ Mips_got_info<size, big_endian>::add_tls_entries(
         gold_unreachable();
     }
 }
+
+// Decide whether the symbol needs an entry in the global part of the primary
+// GOT, setting global_got_area accordingly.  Count the number of global
+// symbols that are in the primary GOT only because they have dynamic
+// relocations R_MIPS_REL32 against them (reloc_only_gotno).
+
+template<int size, bool big_endian>
+void
+Mips_got_info<size, big_endian>::count_got_symbols(Symbol_table* symtab)
+{
+  for (typename Unordered_set<Mips_symbol<size>*>::iterator
+       p = this->global_got_symbols_.begin();
+       p != this->global_got_symbols_.end();
+       ++p)
+    {
+      Mips_symbol<size>* sym = *p;
+      // Make a final decision about whether the symbol belongs in the
+      // local or global GOT.  Symbols that bind locally can (and in the
+      // case of forced-local symbols, must) live in the local GOT.
+      // Those that are aren't in the dynamic symbol table must also
+      // live in the local GOT.
+
+      if (!sym->should_add_dynsym_entry(symtab)
+          || (sym->got_only_for_calls()
+              ? symbol_calls_local(sym, sym->should_add_dynsym_entry(symtab))
+              : symbol_references_local(sym,
+                                        sym->should_add_dynsym_entry(symtab))))
+        // The symbol belongs in the local GOT.  We no longer need this
+        // entry if it was only used for relocations; those relocations
+        // will be against the null or section symbol instead.
+        sym->set_global_got_area(GGA_NONE);
+      else if (sym->global_got_area() == GGA_RELOC_ONLY)
+        {
+          ++this->reloc_only_gotno_;
+          ++this->global_gotno_ ;
+        }
+    }
+}
+
+// Return the offset of GOT page entry for VALUE.  Initialize the entry with
+// VALUE if it is not initialized.
+
+template<int size, bool big_endian>
+unsigned int
+Mips_got_info<size, big_endian>::get_got_page_offset(Mips_address value,
+    Mips_output_data_got<size, big_endian>* got)
+{
+  typename Got_page_offsets::iterator it = this->got_page_offsets_.find(value);
+  if (it != this->got_page_offsets_.end())
+    return it->second;
+
+  gold_assert(this->got_page_offset_next_ < this->got_page_offset_start_
+              + (size/8) * this->page_gotno_);
+
+  unsigned int got_offset = this->got_page_offset_next_;
+  this->got_page_offsets_[value] = got_offset;
+  this->got_page_offset_next_ += size/8;
+  got->update_got_entry(got_offset, value);
+  return got_offset;
+}
+
+// Remove lazy-binding stubs for global symbols in this GOT.
+
+template<int size, bool big_endian>
+void
+Mips_got_info<size, big_endian>::remove_lazy_stubs(
+    Target_mips<size, big_endian>* target)
+{
+  for (typename Got_entry_set::iterator
+       p = this->got_entries_.begin();
+       p != this->got_entries_.end();
+       ++p)
+    {
+      Mips_got_entry<size, big_endian>* entry = *p;
+      if (entry->is_for_global_symbol())
+        target->remove_lazy_stub_entry(entry->sym());
+    }
+}
+
+// Count the number of GOT entries required.
+
+template<int size, bool big_endian>
+void
+Mips_got_info<size, big_endian>::count_got_entries()
+{
+  for (typename Got_entry_set::iterator
+       p = this->got_entries_.begin();
+       p != this->got_entries_.end();
+       ++p)
+    {
+      this->count_got_entry(*p);
+    }
+}
+
+// Count the number of GOT entries required by ENTRY.  Accumulate the result.
+
+template<int size, bool big_endian>
+void
+Mips_got_info<size, big_endian>::count_got_entry(
+    Mips_got_entry<size, big_endian>* entry)
+{
+  if (entry->is_tls_entry())
+    this->tls_gotno_ += mips_tls_got_entries(entry->tls_type());
+  else if (entry->is_for_local_symbol()
+           || entry->sym()->global_got_area() == GGA_NONE)
+    ++this->local_gotno_;
+  else
+    ++this->global_gotno_;
+}
+
+// Add FROM's GOT entries.
+
+template<int size, bool big_endian>
+void
+Mips_got_info<size, big_endian>::add_got_entries(
+    Mips_got_info<size, big_endian>* from)
+{
+  for (typename Got_entry_set::iterator
+       p = from->got_entries_.begin();
+       p != from->got_entries_.end();
+       ++p)
+    {
+      Mips_got_entry<size, big_endian>* entry = *p;
+      if (this->got_entries_.find(entry) == this->got_entries_.end())
+        {
+          Mips_got_entry<size, big_endian>* entry2 =
+            new Mips_got_entry<size, big_endian>(*entry);
+          this->got_entries_.insert(entry2);
+          this->count_got_entry(entry);
+        }
+    }
+}
+
+// Add FROM's GOT page entries.
+
+template<int size, bool big_endian>
+void
+Mips_got_info<size, big_endian>::add_got_page_entries(
+    Mips_got_info<size, big_endian>* from)
+{
+  for (typename Got_page_entry_set::iterator
+       p = from->got_page_entries_.begin();
+       p != from->got_page_entries_.end();
+       ++p)
+    {
+      Got_page_entry* entry = *p;
+      if (this->got_page_entries_.find(entry) == this->got_page_entries_.end())
+        {
+          Got_page_entry* entry2 = new Got_page_entry(*entry);
+          this->got_page_entries_.insert(entry2);
+          this->page_gotno_ += entry->num_pages;
+        }
+    }
+}
+
+// Mips_output_data_got methods.
+
+// Lay out the GOT.  Add local, global and TLS entries.  If GOT is
+// larger than 64K, create multi-GOT.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_got<size, big_endian>::lay_out_got(Layout* layout,
+    Symbol_table* symtab, const Input_objects* input_objects)
+{
+  // Decide which symbols need to go in the global part of the GOT and
+  // count the number of reloc-only GOT symbols.
+  this->master_got_info_->count_got_symbols(symtab);
+
+  // Count the number of GOT entries.
+  this->master_got_info_->count_got_entries();
+
+  unsigned int got_size = this->master_got_info_->got_size();
+  if (got_size > Target_mips<size, big_endian>::MIPS_GOT_MAX_SIZE)
+    this->lay_out_multi_got(layout, input_objects);
+  else
+    {
+      // Record that all objects use single GOT.
+      for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
+           p != input_objects->relobj_end();
+           ++p)
+        {
+          Mips_relobj<size, big_endian>* object =
+            Mips_relobj<size, big_endian>::as_mips_relobj(*p);
+          if (object->get_got_info() != NULL)
+            object->set_got_info(this->master_got_info_);
+        }
+
+      this->master_got_info_->add_local_entries(this->target_, layout);
+      this->master_got_info_->add_global_entries(this->target_, layout,
+                                                 /*not used*/-1U);
+      this->master_got_info_->add_tls_entries(this->target_, layout);
+    }
+}
+
+// Create multi-GOT.  For every GOT, add local, global and TLS entries.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_got<size, big_endian>::lay_out_multi_got(Layout* layout,
+    const Input_objects* input_objects)
+{
+  // Try to merge the GOTs of input objects together, as long as they
+  // don't seem to exceed the maximum GOT size, choosing one of them
+  // to be the primary GOT.
+  this->merge_gots(input_objects);
+
+  // Every symbol that is referenced in a dynamic relocation must be
+  // present in the primary GOT.
+  this->primary_got_->set_global_gotno(this->master_got_info_->global_gotno());
+
+  // Add GOT entries.
+  unsigned int i = 0;
+  unsigned int offset = 0;
+  Mips_got_info<size, big_endian>* g = this->primary_got_;
+  do
+    {
+      g->set_index(i);
+      g->set_offset(offset);
+
+      g->add_local_entries(this->target_, layout);
+      if (i == 0)
+        g->add_global_entries(this->target_, layout,
+                              (this->master_got_info_->global_gotno()
+                               - this->master_got_info_->reloc_only_gotno()));
+      else
+        g->add_global_entries(this->target_, layout, /*not used*/-1U);
+      g->add_tls_entries(this->target_, layout);
+
+      // Forbid global symbols in every non-primary GOT from having
+      // lazy-binding stubs.
+      if (i > 0)
+        g->remove_lazy_stubs(this->target_);
+
+      ++i;
+      offset += g->got_size();
+      g = g->next();
+    }
+  while (g);
+}
+
+// Attempt to merge GOTs of different input objects.  Try to use as much as
+// possible of the primary GOT, since it doesn't require explicit dynamic
+// relocations, but don't use objects that would reference global symbols
+// out of the addressable range.  Failing the primary GOT, attempt to merge
+// with the current GOT, or finish the current GOT and then make make the new
+// GOT current.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_got<size, big_endian>::merge_gots(
+    const Input_objects* input_objects)
+{
+  gold_assert(this->primary_got_ == NULL);
+  Mips_got_info<size, big_endian>* current = NULL;
+
+  for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
+       p != input_objects->relobj_end();
+       ++p)
+    {
+      Mips_relobj<size, big_endian>* object =
+        Mips_relobj<size, big_endian>::as_mips_relobj(*p);
+
+      Mips_got_info<size, big_endian>* g = object->get_got_info();
+      if (g == NULL)
+        continue;
+
+      g->count_got_entries();
+
+      // Work out the number of page, local and TLS entries.
+      unsigned int estimate = this->master_got_info_->page_gotno();
+      if (estimate > g->page_gotno())
+        estimate = g->page_gotno();
+      estimate += g->local_gotno() + g->tls_gotno();
+
+      // We place TLS GOT entries after both locals and globals.  The globals
+      // for the primary GOT may overflow the normal GOT size limit, so be
+      // sure not to merge a GOT which requires TLS with the primary GOT in that
+      // case.  This doesn't affect non-primary GOTs.
+      estimate += (g->tls_gotno() > 0 ? this->master_got_info_->global_gotno()
+                                      : g->global_gotno());
+
+      unsigned int max_count =
+        Target_mips<size, big_endian>::MIPS_GOT_MAX_SIZE / (size/8) - 2;
+      if (estimate <= max_count)
+        {
+          // If we don't have a primary GOT, use it as
+          // a starting point for the primary GOT.
+          if (!this->primary_got_)
+            {
+              this->primary_got_ = g;
+              continue;
+            }
+
+          // Try merging with the primary GOT.
+          if (this->merge_got_with(g, object, this->primary_got_))
+            continue;
+        }
+
+      // If we can merge with the last-created GOT, do it.
+      if (current && this->merge_got_with(g, object, current))
+        continue;
+
+      // Well, we couldn't merge, so create a new GOT.  Don't check if it
+      // fits; if it turns out that it doesn't, we'll get relocation
+      // overflows anyway.
+      g->set_next(current);
+      current = g;
+    }
+
+  // If we do not find any suitable primary GOT, create an empty one.
+  if (this->primary_got_ == NULL)
+    this->primary_got_ = new Mips_got_info<size, big_endian>();
+
+  // Link primary GOT with secondary GOTs.
+  this->primary_got_->set_next(current);
+}
+
+// Consider merging FROM, which is OBJECT's GOT, into TO.  Return false if
+// this would lead to overflow, true if they were merged successfully.
+
+template<int size, bool big_endian>
+bool
+Mips_output_data_got<size, big_endian>::merge_got_with(
+    Mips_got_info<size, big_endian>* from,
+    Mips_relobj<size, big_endian>* object,
+    Mips_got_info<size, big_endian>* to)
+{
+  // Work out how many page entries we would need for the combined GOT.
+  unsigned int estimate = this->master_got_info_->page_gotno();
+  if (estimate >= from->page_gotno() + to->page_gotno())
+    estimate = from->page_gotno() + to->page_gotno();
+
+  // Conservatively estimate how many local and TLS entries would be needed.
+  estimate += from->local_gotno() + to->local_gotno();
+  estimate += from->tls_gotno() + to->tls_gotno();
+
+  // If we're merging with the primary got, any TLS relocations will
+  // come after the full set of global entries.  Otherwise estimate those
+  // conservatively as well.
+  if (to == this->primary_got_ && (from->tls_gotno() + to->tls_gotno()) > 0)
+    estimate += this->master_got_info_->global_gotno();
+  else
+    estimate += from->global_gotno() + to->global_gotno();
+
+  // Bail out if the combined GOT might be too big.
+  unsigned int max_count =
+    Target_mips<size, big_endian>::MIPS_GOT_MAX_SIZE / (size/8) - 2;
+  if (estimate > max_count)
+    return false;
+
+  // Transfer the object's GOT information from FROM to TO.
+  to->add_got_entries(from);
+  to->add_got_page_entries(from);
+
+  // Record that OBJECT should use output GOT TO.
+  object->set_got_info(to);
+
+  return true;
+}
+
+// Write out the GOT.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_got<size, big_endian>::do_write(Output_file* of)
+{
+  // Call parent to write out GOT.
+  Output_data_got<size, big_endian>::do_write(of);
+
+  const off_t offset = this->offset();
+  const section_size_type oview_size =
+    convert_to_section_size_type(this->data_size());
+  unsigned char* const oview = of->get_output_view(offset, oview_size);
+
+  // Needed for fixing values of .got section.
+  this->got_view_ = oview;
+
+  // Write lazy stub addresses.
+  for (typename Unordered_set<Mips_symbol<size>*>::iterator
+       p = this->master_got_info_->global_got_symbols().begin();
+       p != this->master_got_info_->global_got_symbols().end();
+       ++p)
+    {
+      Mips_symbol<size>* mips_sym = *p;
+      if (mips_sym->has_lazy_stub())
+        {
+          Valtype* wv = reinterpret_cast<Valtype*>(
+            oview + this->get_primary_got_offset(mips_sym));
+          Valtype value =
+            this->target_->mips_stubs_section()->stub_address(mips_sym);
+          elfcpp::Swap<size, big_endian>::writeval(wv, value);
+        }
+    }
+
+  // Add +1 to GGA_NONE nonzero MIPS16 and microMIPS entries.
+  for (typename Unordered_set<Mips_symbol<size>*>::iterator
+       p = this->master_got_info_->global_got_symbols().begin();
+       p != this->master_got_info_->global_got_symbols().end();
+       ++p)
+    {
+      Mips_symbol<size>* mips_sym = *p;
+      if (!this->multi_got()
+          && (mips_sym->is_mips16() || mips_sym->is_micromips())
+          && mips_sym->global_got_area() == GGA_NONE
+          && mips_sym->has_got_offset(GOT_TYPE_STANDARD))
+        {
+          Valtype* wv = reinterpret_cast<Valtype*>(
+            oview + mips_sym->got_offset(GOT_TYPE_STANDARD));
+          Valtype value = elfcpp::Swap<size, big_endian>::readval(wv);
+          if (value != 0)
+            {
+              value |= 1;
+              elfcpp::Swap<size, big_endian>::writeval(wv, value);
+            }
+        }
+    }
+
+  if (!this->secondary_got_relocs_.empty())
+    {
+      // Fixup for the secondary GOT R_MIPS_REL32 relocs.  For global
+      // secondary GOT entries with non-zero initial value copy the value
+      // to the corresponding primary GOT entry, and set the secondary GOT
+      // entry to zero.
+      // TODO(sasa): This is workaround.  It needs to be investigated further.
+
+      for (size_t i = 0; i < this->secondary_got_relocs_.size(); ++i)
+        {
+          Static_reloc& reloc(this->secondary_got_relocs_[i]);
+          if (reloc.symbol_is_global())
+            {
+              Mips_symbol<size>* gsym = reloc.symbol();
+              gold_assert(gsym != NULL);
+
+              unsigned got_offset = reloc.got_offset();
+              gold_assert(got_offset < oview_size);
+
+              // Find primary GOT entry.
+              Valtype* wv_prim = reinterpret_cast<Valtype*>(
+                oview + this->get_primary_got_offset(gsym));
+
+              // Find secondary GOT entry.
+              Valtype* wv_sec = reinterpret_cast<Valtype*>(oview + got_offset);
+
+              Valtype value = elfcpp::Swap<size, big_endian>::readval(wv_sec);
+              if (value != 0)
+                {
+                  elfcpp::Swap<size, big_endian>::writeval(wv_prim, value);
+                  elfcpp::Swap<size, big_endian>::writeval(wv_sec, 0);
+                  gsym->set_applied_secondary_got_fixup();
+                }
+            }
+        }
+
+      of->write_output_view(offset, oview_size, oview);
+    }
+
+  // We are done if there is no fix up.
+  if (this->static_relocs_.empty())
+    return;
+
+  Output_segment* tls_segment = this->layout_->tls_segment();
+  gold_assert(tls_segment != NULL);
+
+  for (size_t i = 0; i < this->static_relocs_.size(); ++i)
+    {
+      Static_reloc& reloc(this->static_relocs_[i]);
+
+      Mips_address value;
+      if (!reloc.symbol_is_global())
+        {
+          Sized_relobj_file<size, big_endian>* object = reloc.relobj();
+          const Symbol_value<size>* psymval =
+            object->local_symbol(reloc.index());
+
+          // We are doing static linking.  Issue an error and skip this
+          // relocation if the symbol is undefined or in a discarded_section.
+          bool is_ordinary;
+          unsigned int shndx = psymval->input_shndx(&is_ordinary);
+          if ((shndx == elfcpp::SHN_UNDEF)
+              || (is_ordinary
+                  && shndx != elfcpp::SHN_UNDEF
+                  && !object->is_section_included(shndx)
+                  && !this->symbol_table_->is_section_folded(object, shndx)))
+            {
+              gold_error(_("undefined or discarded local symbol %u from "
+                           " object %s in GOT"),
+                         reloc.index(), reloc.relobj()->name().c_str());
+              continue;
+            }
+
+          value = psymval->value(object, 0);
+        }
+      else
+        {
+          const Mips_symbol<size>* gsym = reloc.symbol();
+          gold_assert(gsym != NULL);
+
+          // We are doing static linking.  Issue an error and skip this
+          // relocation if the symbol is undefined or in a discarded_section
+          // unless it is a weakly_undefined symbol.
+          if ((gsym->is_defined_in_discarded_section() || gsym->is_undefined())
+              && !gsym->is_weak_undefined())
+            {
+              gold_error(_("undefined or discarded symbol %s in GOT"),
+                         gsym->name());
+              continue;
+            }
+
+          if (!gsym->is_weak_undefined())
+            value = gsym->value();
+          else
+            value = 0;
+        }
+
+      unsigned got_offset = reloc.got_offset();
+      gold_assert(got_offset < oview_size);
+
+      Valtype* wv = reinterpret_cast<Valtype*>(oview + got_offset);
+      Valtype x;
+
+      switch (reloc.r_type())
+        {
+        case elfcpp::R_MIPS_TLS_DTPMOD32:
+        case elfcpp::R_MIPS_TLS_DTPMOD64:
+          x = value;
+          break;
+        case elfcpp::R_MIPS_TLS_DTPREL32:
+        case elfcpp::R_MIPS_TLS_DTPREL64:
+          x = value - elfcpp::DTP_OFFSET;
+          break;
+        case elfcpp::R_MIPS_TLS_TPREL32:
+        case elfcpp::R_MIPS_TLS_TPREL64:
+          x = value - elfcpp::TP_OFFSET;
+          break;
+        default:
+          gold_unreachable();
+          break;
+        }
+
+      elfcpp::Swap<size, big_endian>::writeval(wv, x);
+    }
+
+  of->write_output_view(offset, oview_size, oview);
+}
+
+// Mips_relobj methods.
+
+// Count the local symbols.  The Mips backend needs to know if a symbol
+// is a MIPS16 or microMIPS function or not.  For global symbols, it is easy
+// because the Symbol object keeps the ELF symbol type and st_other field.
+// For local symbol it is harder because we cannot access this information.
+// So we override the do_count_local_symbol in parent and scan local symbols to
+// mark MIPS16 and microMIPS functions.  This is not the most efficient way but
+// I do not want to slow down other ports by calling a per symbol target hook
+// inside Sized_relobj_file<size, big_endian>::do_count_local_symbols.
+
+template<int size, bool big_endian>
+void
+Mips_relobj<size, big_endian>::do_count_local_symbols(
+    Stringpool_template<char>* pool,
+    Stringpool_template<char>* dynpool)
+{
+  // Ask parent to count the local symbols.
+  Sized_relobj_file<size, big_endian>::do_count_local_symbols(pool, dynpool);
+  const unsigned int loccount = this->local_symbol_count();
+  if (loccount == 0)
+    return;
+
+  // Initialize the mips16 and micromips function bit-vector.
+  this->local_symbol_is_mips16_.resize(loccount, false);
+  this->local_symbol_is_micromips_.resize(loccount, false);
+
+  // Read the symbol table section header.
+  const unsigned int symtab_shndx = this->symtab_shndx();
+  elfcpp::Shdr<size, big_endian>
+    symtabshdr(this, this->elf_file()->section_header(symtab_shndx));
+  gold_assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB);
+
+  // Read the local symbols.
+  const int sym_size = elfcpp::Elf_sizes<size>::sym_size;
+  gold_assert(loccount == symtabshdr.get_sh_info());
+  off_t locsize = loccount * sym_size;
+  const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(),
+                                              locsize, true, true);
+
+  // Loop over the local symbols and mark any MIPS16 or microMIPS local symbols.
+
+  // Skip the first dummy symbol.
+  psyms += sym_size;
+  for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size)
+    {
+      elfcpp::Sym<size, big_endian> sym(psyms);
+      unsigned char st_other = sym.get_st_other();
+      this->local_symbol_is_mips16_[i] = elfcpp::elf_st_is_mips16(st_other);
+      this->local_symbol_is_micromips_[i] =
+        elfcpp::elf_st_is_micromips(st_other);
+    }
+}
+
+// Read the symbol information.
+
+template<int size, bool big_endian>
+void
+Mips_relobj<size, big_endian>::do_read_symbols(Read_symbols_data* sd)
+{
+  // Call parent class to read symbol information.
+  Sized_relobj_file<size, big_endian>::do_read_symbols(sd);
+
+  // Read processor-specific flags in ELF file header.
+  const unsigned char* pehdr = this->get_view(elfcpp::file_header_offset,
+                                            elfcpp::Elf_sizes<size>::ehdr_size,
+                                            true, false);
+  elfcpp::Ehdr<size, big_endian> ehdr(pehdr);
+  this->processor_specific_flags_ = ehdr.get_e_flags();
+
+  // Get the section names.
+  const unsigned char* pnamesu = sd->section_names->data();
+  const char* pnames = reinterpret_cast<const char*>(pnamesu);
+
+  // Initialize the mips16 stub section bit-vectors.
+  this->section_is_mips16_fn_stub_.resize(this->shnum(), false);
+  this->section_is_mips16_call_stub_.resize(this->shnum(), false);
+  this->section_is_mips16_call_fp_stub_.resize(this->shnum(), false);
+
+  const size_t shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
+  const unsigned char* pshdrs = sd->section_headers->data();
+  const unsigned char* ps = pshdrs + shdr_size;
+  for (unsigned int i = 1; i < this->shnum(); ++i, ps += shdr_size)
+    {
+      elfcpp::Shdr<size, big_endian> shdr(ps);
+
+      if (shdr.get_sh_type() == elfcpp::SHT_MIPS_REGINFO)
+        {
+          // Read the gp value that was used to create this object.  We need the
+          // gp value while processing relocs.  The .reginfo section is not used
+          // in the 64-bit MIPS ELF ABI.
+          section_offset_type section_offset = shdr.get_sh_offset();
+          section_size_type section_size =
+            convert_to_section_size_type(shdr.get_sh_size());
+          const unsigned char* view =
+             this->get_view(section_offset, section_size, true, false);
+
+          this->gp_ = elfcpp::Swap<size, big_endian>::readval(view + 20);
+
+          // Read the rest of .reginfo.
+          this->gprmask_ = elfcpp::Swap<size, big_endian>::readval(view);
+          this->cprmask1_ = elfcpp::Swap<size, big_endian>::readval(view + 4);
+          this->cprmask2_ = elfcpp::Swap<size, big_endian>::readval(view + 8);
+          this->cprmask3_ = elfcpp::Swap<size, big_endian>::readval(view + 12);
+          this->cprmask4_ = elfcpp::Swap<size, big_endian>::readval(view + 16);
+        }
+
+      const char* name = pnames + shdr.get_sh_name();
+      this->section_is_mips16_fn_stub_[i] = is_prefix_of(".mips16.fn", name);
+      this->section_is_mips16_call_stub_[i] =
+        is_prefix_of(".mips16.call.", name);
+      this->section_is_mips16_call_fp_stub_[i] =
+        is_prefix_of(".mips16.call.fp.", name);
+
+      if (strcmp(name, ".pdr") == 0)
+        {
+          gold_assert(this->pdr_shndx_ == -1U);
+          this->pdr_shndx_ = i;
+        }
+    }
+}
+
+// Discard MIPS16 stub secions that are not needed.
+
+template<int size, bool big_endian>
+void
+Mips_relobj<size, big_endian>::discard_mips16_stub_sections(Symbol_table* symtab)
+{
+  for (typename Mips16_stubs_int_map::const_iterator
+       it = this->mips16_stub_sections_.begin();
+       it != this->mips16_stub_sections_.end(); ++it)
+    {
+      Mips16_stub_section<size, big_endian>* stub_section = it->second;
+      if (!stub_section->is_target_found())
+        {
+          gold_error(_("no relocation found in mips16 stub section '%s'"),
+                     stub_section->object()
+                       ->section_name(stub_section->shndx()).c_str());
+        }
+
+      bool discard = false;
+      if (stub_section->is_for_local_function())
+        {
+          if (stub_section->is_fn_stub())
+            {
+              // This stub is for a local symbol.  This stub will only
+              // be needed if there is some relocation in this object,
+              // other than a 16 bit function call, which refers to this
+              // symbol.
+              if (!this->has_local_non_16bit_call_relocs(stub_section->r_sym()))
+                discard = true;
+              else
+                this->add_local_mips16_fn_stub(stub_section);
+            }
+          else
+            {
+              // This stub is for a local symbol.  This stub will only
+              // be needed if there is some relocation (R_MIPS16_26) in
+              // this object that refers to this symbol.
+              gold_assert(stub_section->is_call_stub()
+                          || stub_section->is_call_fp_stub());
+              if (!this->has_local_16bit_call_relocs(stub_section->r_sym()))
+                discard = true;
+              else
+                this->add_local_mips16_call_stub(stub_section);
+            }
+        }
+      else
+        {
+          Mips_symbol<size>* gsym = stub_section->gsym();
+          if (stub_section->is_fn_stub())
+            {
+              if (gsym->has_mips16_fn_stub())
+                // We already have a stub for this function.
+                discard = true;
+              else
+                {
+                  gsym->set_mips16_fn_stub(stub_section);
+                  if (gsym->should_add_dynsym_entry(symtab))
+                    {
+                      // If we have a MIPS16 function with a stub, the
+                      // dynamic symbol must refer to the stub, since only
+                      // the stub uses the standard calling conventions.
+                      gsym->set_need_fn_stub();
+                      if (gsym->is_from_dynobj())
+                        gsym->set_needs_dynsym_value();
+                    }
+                }
+              if (!gsym->need_fn_stub())
+                discard = true;
+            }
+          else if (stub_section->is_call_stub())
+            {
+              if (gsym->is_mips16())
+                // We don't need the call_stub; this is a 16 bit
+                // function, so calls from other 16 bit functions are
+                // OK.
+                discard = true;
+              else if (gsym->has_mips16_call_stub())
+                // We already have a stub for this function.
+                discard = true;
+              else
+                gsym->set_mips16_call_stub(stub_section);
+            }
+          else
+            {
+              gold_assert(stub_section->is_call_fp_stub());
+              if (gsym->is_mips16())
+                // We don't need the call_stub; this is a 16 bit
+                // function, so calls from other 16 bit functions are
+                // OK.
+                discard = true;
+              else if (gsym->has_mips16_call_fp_stub())
+                // We already have a stub for this function.
+                discard = true;
+              else
+                gsym->set_mips16_call_fp_stub(stub_section);
+            }
+        }
+      if (discard)
+        this->set_output_section(stub_section->shndx(), NULL);
+   }
+}
+
+// Mips_output_data_la25_stub methods.
+
+// Template for standard LA25 stub.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_la25_stub<size, big_endian>::la25_stub_entry[] =
+{
+  0x3c190000,           // lui $25,%hi(func)
+  0x08000000,           // j func
+  0x27390000,           // add $25,$25,%lo(func)
+  0x00000000            // nop
+};
+
+// Template for microMIPS LA25 stub.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_la25_stub<size, big_endian>::la25_stub_micromips_entry[] =
+{
+  0x41b9, 0x0000,       // lui t9,%hi(func)
+  0xd400, 0x0000,       // j func
+  0x3339, 0x0000,       // addiu t9,t9,%lo(func)
+  0x0000, 0x0000        // nop
+};
+
+// Create la25 stub for a symbol.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_la25_stub<size, big_endian>::create_la25_stub(
+    Symbol_table* symtab, Target_mips<size, big_endian>* target,
+    Mips_symbol<size>* gsym)
+{
+  if (!gsym->has_la25_stub())
+    {
+      gsym->set_la25_stub_offset(this->symbols_.size() * 16);
+      this->symbols_.insert(gsym);
+      this->create_stub_symbol(gsym, symtab, target, 16);
+    }
+}
+
+// Create a symbol for SYM stub's value and size, to help make the disassembly
+// easier to read.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_la25_stub<size, big_endian>::create_stub_symbol(
+    Mips_symbol<size>* sym, Symbol_table* symtab,
+    Target_mips<size, big_endian>* target, uint64_t symsize)
+{
+  std::string name(".pic.");
+  name += sym->name();
+
+  unsigned int offset = sym->la25_stub_offset();
+  if (sym->is_micromips())
+    offset |= 1;
+
+  // Make it a local function.
+  Symbol* new_sym = symtab->define_in_output_data(name.c_str(), NULL,
+                                      Symbol_table::PREDEFINED,
+                                      target->la25_stub_section(),
+                                      offset, symsize, elfcpp::STT_FUNC,
+                                      elfcpp::STB_LOCAL,
+                                      elfcpp::STV_DEFAULT, 0,
+                                      false, false);
+  new_sym->set_is_forced_local();
+}
+
+// Write out la25 stubs.  This uses the hand-coded instructions above,
+// and adjusts them as needed.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_la25_stub<size, big_endian>::do_write(Output_file* of)
+{
+  const off_t offset = this->offset();
+  const section_size_type oview_size =
+    convert_to_section_size_type(this->data_size());
+  unsigned char* const oview = of->get_output_view(offset, oview_size);
+
+  for (typename Unordered_set<Mips_symbol<size>*>::iterator
+       p = this->symbols_.begin();
+       p != this->symbols_.end();
+       ++p)
+    {
+      Mips_symbol<size>* sym = *p;
+      unsigned char* pov = oview + sym->la25_stub_offset();
+
+      Mips_address target = sym->value();
+      if (!sym->is_micromips())
+        {
+          elfcpp::Swap<32, big_endian>::writeval(pov,
+              la25_stub_entry[0] | (((target + 0x8000) >> 16) & 0xffff));
+          elfcpp::Swap<32, big_endian>::writeval(pov + 4,
+              la25_stub_entry[1] | ((target >> 2) & 0x3ffffff));
+          elfcpp::Swap<32, big_endian>::writeval(pov + 8,
+              la25_stub_entry[2] | (target & 0xffff));
+          elfcpp::Swap<32, big_endian>::writeval(pov + 12, la25_stub_entry[3]);
+        }
+      else
+        {
+          target |= 1;
+          // First stub instruction.  Paste high 16-bits of the target.
+          elfcpp::Swap<16, big_endian>::writeval(pov,
+                                                 la25_stub_micromips_entry[0]);
+          elfcpp::Swap<16, big_endian>::writeval(pov + 2,
+              ((target + 0x8000) >> 16) & 0xffff);
+          // Second stub instruction.  Paste low 26-bits of the target, shifted
+          // right by 1.
+          elfcpp::Swap<16, big_endian>::writeval(pov + 4,
+              la25_stub_micromips_entry[2] | ((target >> 17) & 0x3ff));
+          elfcpp::Swap<16, big_endian>::writeval(pov + 6,
+              la25_stub_micromips_entry[3] | ((target >> 1) & 0xffff));
+          // Third stub instruction.  Paste low 16-bits of the target.
+          elfcpp::Swap<16, big_endian>::writeval(pov + 8,
+                                                 la25_stub_micromips_entry[4]);
+          elfcpp::Swap<16, big_endian>::writeval(pov + 10, target & 0xffff);
+          // Fourth stub instruction.
+          elfcpp::Swap<16, big_endian>::writeval(pov + 12,
+                                                 la25_stub_micromips_entry[6]);
+          elfcpp::Swap<16, big_endian>::writeval(pov + 14,
+                                                 la25_stub_micromips_entry[7]);
+        }
+    }
+
+  of->write_output_view(offset, oview_size, oview);
+}
+
+// Mips_output_data_plt methods.
+
+// The format of the first PLT entry in an O32 executable.
+template<int size, bool big_endian>
+const uint32_t Mips_output_data_plt<size, big_endian>::plt0_entry_o32[] =
+{
+  0x3c1c0000,         // lui $28, %hi(&GOTPLT[0])
+  0x8f990000,         // lw $25, %lo(&GOTPLT[0])($28)
+  0x279c0000,         // addiu $28, $28, %lo(&GOTPLT[0])
+  0x031cc023,         // subu $24, $24, $28
+  0x03e07821,         // move $15, $31        # 32-bit move (addu)
+  0x0018c082,         // srl $24, $24, 2
+  0x0320f809,         // jalr $25
+  0x2718fffe          // subu $24, $24, 2
+};
+
+// The format of the first PLT entry in an N32 executable.  Different
+// because gp ($28) is not available; we use t2 ($14) instead.
+template<int size, bool big_endian>
+const uint32_t Mips_output_data_plt<size, big_endian>::plt0_entry_n32[] =
+{
+  0x3c0e0000,         // lui $14, %hi(&GOTPLT[0])
+  0x8dd90000,         // lw $25, %lo(&GOTPLT[0])($14)
+  0x25ce0000,         // addiu $14, $14, %lo(&GOTPLT[0])
+  0x030ec023,         // subu $24, $24, $14
+  0x03e07821,         // move $15, $31        # 32-bit move (addu)
+  0x0018c082,         // srl $24, $24, 2
+  0x0320f809,         // jalr $25
+  0x2718fffe          // subu $24, $24, 2
+};
+
+// The format of the first PLT entry in an N64 executable.  Different
+// from N32 because of the increased size of GOT entries.
+template<int size, bool big_endian>
+const uint32_t Mips_output_data_plt<size, big_endian>::plt0_entry_n64[] =
+{
+  0x3c0e0000,         // lui $14, %hi(&GOTPLT[0])
+  0xddd90000,         // ld $25, %lo(&GOTPLT[0])($14)
+  0x25ce0000,         // addiu $14, $14, %lo(&GOTPLT[0])
+  0x030ec023,         // subu $24, $24, $14
+  0x03e07821,         // move $15, $31        # 64-bit move (daddu)
+  0x0018c0c2,         // srl $24, $24, 3
+  0x0320f809,         // jalr $25
+  0x2718fffe          // subu $24, $24, 2
+};
+
+// The format of the microMIPS first PLT entry in an O32 executable.
+// We rely on v0 ($2) rather than t8 ($24) to contain the address
+// of the GOTPLT entry handled, so this stub may only be used when
+// all the subsequent PLT entries are microMIPS code too.
+//
+// The trailing NOP is for alignment and correct disassembly only.
+template<int size, bool big_endian>
+const uint32_t Mips_output_data_plt<size, big_endian>::
+plt0_entry_micromips_o32[] =
+{
+  0x7980, 0x0000,      // addiupc $3, (&GOTPLT[0]) - .
+  0xff23, 0x0000,      // lw $25, 0($3)
+  0x0535,              // subu $2, $2, $3
+  0x2525,              // srl $2, $2, 2
+  0x3302, 0xfffe,      // subu $24, $2, 2
+  0x0dff,              // move $15, $31
+  0x45f9,              // jalrs $25
+  0x0f83,              // move $28, $3
+  0x0c00               // nop
+};
+
+// The format of the microMIPS first PLT entry in an O32 executable
+// in the insn32 mode.
+template<int size, bool big_endian>
+const uint32_t Mips_output_data_plt<size, big_endian>::
+plt0_entry_micromips32_o32[] =
+{
+  0x41bc, 0x0000,      // lui $28, %hi(&GOTPLT[0])
+  0xff3c, 0x0000,      // lw $25, %lo(&GOTPLT[0])($28)
+  0x339c, 0x0000,      // addiu $28, $28, %lo(&GOTPLT[0])
+  0x0398, 0xc1d0,      // subu $24, $24, $28
+  0x001f, 0x7950,      // move $15, $31
+  0x0318, 0x1040,      // srl $24, $24, 2
+  0x03f9, 0x0f3c,      // jalr $25
+  0x3318, 0xfffe       // subu $24, $24, 2
+};
+
+// The format of subsequent standard entries in the PLT.
+template<int size, bool big_endian>
+const uint32_t Mips_output_data_plt<size, big_endian>::plt_entry[] =
+{
+  0x3c0f0000,           // lui $15, %hi(.got.plt entry)
+  0x8df90000,           // l[wd] $25, %lo(.got.plt entry)($15)
+  0x03200008,           // jr $25
+  0x25f80000            // addiu $24, $15, %lo(.got.plt entry)
+};
+
+// The format of subsequent MIPS16 o32 PLT entries.  We use v1 ($3) as a
+// temporary because t8 ($24) and t9 ($25) are not directly addressable.
+// Note that this differs from the GNU ld which uses both v0 ($2) and v1 ($3).
+// We cannot use v0 because MIPS16 call stubs from the CS toolchain expect
+// target function address in register v0.
+template<int size, bool big_endian>
+const uint32_t Mips_output_data_plt<size, big_endian>::plt_entry_mips16_o32[] =
+{
+  0xb303,              // lw $3, 12($pc)
+  0x651b,              // move $24, $3
+  0x9b60,              // lw $3, 0($3)
+  0xeb00,              // jr $3
+  0x653b,              // move $25, $3
+  0x6500,              // nop
+  0x0000, 0x0000       // .word (.got.plt entry)
+};
+
+// The format of subsequent microMIPS o32 PLT entries.  We use v0 ($2)
+// as a temporary because t8 ($24) is not addressable with ADDIUPC.
+template<int size, bool big_endian>
+const uint32_t Mips_output_data_plt<size, big_endian>::
+plt_entry_micromips_o32[] =
+{
+  0x7900, 0x0000,      // addiupc $2, (.got.plt entry) - .
+  0xff22, 0x0000,      // lw $25, 0($2)
+  0x4599,              // jr $25
+  0x0f02               // move $24, $2
+};
+
+// The format of subsequent microMIPS o32 PLT entries in the insn32 mode.
+template<int size, bool big_endian>
+const uint32_t Mips_output_data_plt<size, big_endian>::
+plt_entry_micromips32_o32[] =
+{
+  0x41af, 0x0000,      // lui $15, %hi(.got.plt entry)
+  0xff2f, 0x0000,      // lw $25, %lo(.got.plt entry)($15)
+  0x0019, 0x0f3c,      // jr $25
+  0x330f, 0x0000       // addiu $24, $15, %lo(.got.plt entry)
+};
+
+// Add an entry to the PLT for a symbol referenced by r_type relocation.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_plt<size, big_endian>::add_entry(Mips_symbol<size>* gsym,
+                                                  unsigned int r_type)
+{
+  gold_assert(!gsym->has_plt_offset());
+
+  // Final PLT offset for a symbol will be set in method set_plt_offsets().
+  gsym->set_plt_offset(this->entry_count() * sizeof(plt_entry)
+                       + sizeof(plt0_entry_o32));
+  this->symbols_.push_back(gsym);
+
+  // Record whether the relocation requires a standard MIPS
+  // or a compressed code entry.
+  if (jal_reloc(r_type))
+   {
+     if (r_type == elfcpp::R_MIPS_26)
+       gsym->set_needs_mips_plt(true);
+     else
+       gsym->set_needs_comp_plt(true);
+   }
+
+  section_offset_type got_offset = this->got_plt_->current_data_size();
+
+  // Every PLT entry needs a GOT entry which points back to the PLT
+  // entry (this will be changed by the dynamic linker, normally
+  // lazily when the function is called).
+  this->got_plt_->set_current_data_size(got_offset + size/8);
+
+  gsym->set_needs_dynsym_entry();
+  this->rel_->add_global(gsym, elfcpp::R_MIPS_JUMP_SLOT, this->got_plt_,
+                         got_offset);
+}
+
+// Set final PLT offsets.  For each symbol, determine whether standard or
+// compressed (MIPS16 or microMIPS) PLT entry is used.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_plt<size, big_endian>::set_plt_offsets()
+{
+  // The sizes of individual PLT entries.
+  unsigned int plt_mips_entry_size = this->standard_plt_entry_size();
+  unsigned int plt_comp_entry_size = (!this->target_->is_output_newabi()
+                                      ? this->compressed_plt_entry_size() : 0);
+
+  for (typename std::vector<Mips_symbol<size>*>::const_iterator
+       p = this->symbols_.begin(); p != this->symbols_.end(); ++p)
+    {
+      Mips_symbol<size>* mips_sym = *p;
+
+      // There are no defined MIPS16 or microMIPS PLT entries for n32 or n64,
+      // so always use a standard entry there.
+      //
+      // If the symbol has a MIPS16 call stub and gets a PLT entry, then
+      // all MIPS16 calls will go via that stub, and there is no benefit
+      // to having a MIPS16 entry.  And in the case of call_stub a
+      // standard entry actually has to be used as the stub ends with a J
+      // instruction.
+      if (this->target_->is_output_newabi()
+          || mips_sym->has_mips16_call_stub()
+          || mips_sym->has_mips16_call_fp_stub())
+        {
+          mips_sym->set_needs_mips_plt(true);
+          mips_sym->set_needs_comp_plt(false);
+        }
+
+      // Otherwise, if there are no direct calls to the function, we
+      // have a free choice of whether to use standard or compressed
+      // entries.  Prefer microMIPS entries if the object is known to
+      // contain microMIPS code, so that it becomes possible to create
+      // pure microMIPS binaries.  Prefer standard entries otherwise,
+      // because MIPS16 ones are no smaller and are usually slower.
+      if (!mips_sym->needs_mips_plt() && !mips_sym->needs_comp_plt())
+        {
+          if (this->target_->is_output_micromips())
+            mips_sym->set_needs_comp_plt(true);
+          else
+            mips_sym->set_needs_mips_plt(true);
+        }
+
+      if (mips_sym->needs_mips_plt())
+        {
+          mips_sym->set_mips_plt_offset(this->plt_mips_offset_);
+          this->plt_mips_offset_ += plt_mips_entry_size;
+        }
+      if (mips_sym->needs_comp_plt())
+        {
+          mips_sym->set_comp_plt_offset(this->plt_comp_offset_);
+          this->plt_comp_offset_ += plt_comp_entry_size;
+        }
+    }
+
+    // Figure out the size of the PLT header if we know that we are using it.
+    if (this->plt_mips_offset_ + this->plt_comp_offset_ != 0)
+      this->plt_header_size_ = this->get_plt_header_size();
+}
+
+// Write out the PLT.  This uses the hand-coded instructions above,
+// and adjusts them as needed.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_plt<size, big_endian>::do_write(Output_file* of)
+{
+  const off_t offset = this->offset();
+  const section_size_type oview_size =
+    convert_to_section_size_type(this->data_size());
+  unsigned char* const oview = of->get_output_view(offset, oview_size);
+
+  const off_t gotplt_file_offset = this->got_plt_->offset();
+  const section_size_type gotplt_size =
+    convert_to_section_size_type(this->got_plt_->data_size());
+  unsigned char* const gotplt_view = of->get_output_view(gotplt_file_offset,
+                                                         gotplt_size);
+  unsigned char* pov = oview;
+
+  Mips_address plt_address = this->address();
+
+  // Calculate the address of .got.plt.
+  Mips_address gotplt_addr = this->got_plt_->address();
+  Mips_address gotplt_addr_high = ((gotplt_addr + 0x8000) >> 16) & 0xffff;
+  Mips_address gotplt_addr_low = gotplt_addr & 0xffff;
+
+  // The PLT sequence is not safe for N64 if .got.plt's address can
+  // not be loaded in two instructions.
+  gold_assert((gotplt_addr & ~(Mips_address) 0x7fffffff) == 0
+              || ~(gotplt_addr | 0x7fffffff) == 0);
+
+  // Write the PLT header.
+  const uint32_t* plt0_entry = this->get_plt_header_entry();
+  if (plt0_entry == plt0_entry_micromips_o32)
+    {
+      // Write microMIPS PLT header.
+      gold_assert(gotplt_addr % 4 == 0);
+
+      Mips_address gotpc_offset = gotplt_addr - ((plt_address | 3) ^ 3);
+
+      // ADDIUPC has a span of +/-16MB, check we're in range.
+      if (gotpc_offset + 0x1000000 >= 0x2000000)
+       {
+         gold_error(_(".got.plt offset of %ld from .plt beyond the range of "
+                    "ADDIUPC"), (long)gotpc_offset);
+         return;
+       }
+
+      elfcpp::Swap<16, big_endian>::writeval(pov,
+                 plt0_entry[0] | ((gotpc_offset >> 18) & 0x7f));
+      elfcpp::Swap<16, big_endian>::writeval(pov + 2,
+                                             (gotpc_offset >> 2) & 0xffff);
+      pov += 4;
+      for (unsigned int i = 2;
+           i < (sizeof(plt0_entry_micromips_o32)
+                / sizeof(plt0_entry_micromips_o32[0]));
+           i++)
+        {
+          elfcpp::Swap<16, big_endian>::writeval(pov, plt0_entry[i]);
+          pov += 2;
+        }
+    }
+  else if (plt0_entry == plt0_entry_micromips32_o32)
+    {
+      // Write microMIPS PLT header in insn32 mode.
+      elfcpp::Swap<16, big_endian>::writeval(pov, plt0_entry[0]);
+      elfcpp::Swap<16, big_endian>::writeval(pov + 2, gotplt_addr_high);
+      elfcpp::Swap<16, big_endian>::writeval(pov + 4, plt0_entry[2]);
+      elfcpp::Swap<16, big_endian>::writeval(pov + 6, gotplt_addr_low);
+      elfcpp::Swap<16, big_endian>::writeval(pov + 8, plt0_entry[4]);
+      elfcpp::Swap<16, big_endian>::writeval(pov + 10, gotplt_addr_low);
+      pov += 12;
+      for (unsigned int i = 6;
+           i < (sizeof(plt0_entry_micromips32_o32)
+                / sizeof(plt0_entry_micromips32_o32[0]));
+           i++)
+        {
+          elfcpp::Swap<16, big_endian>::writeval(pov, plt0_entry[i]);
+          pov += 2;
+        }
+    }
+  else
+    {
+      // Write standard PLT header.
+      elfcpp::Swap<32, big_endian>::writeval(pov,
+                                             plt0_entry[0] | gotplt_addr_high);
+      elfcpp::Swap<32, big_endian>::writeval(pov + 4,
+                                             plt0_entry[1] | gotplt_addr_low);
+      elfcpp::Swap<32, big_endian>::writeval(pov + 8,
+                                             plt0_entry[2] | gotplt_addr_low);
+      pov += 12;
+      for (int i = 3; i < 8; i++)
+        {
+          elfcpp::Swap<32, big_endian>::writeval(pov, plt0_entry[i]);
+          pov += 4;
+        }
+    }
+
+
+  unsigned char* gotplt_pov = gotplt_view;
+  unsigned int got_entry_size = size/8; // TODO(sasa): MIPS_ELF_GOT_SIZE
+
+  // The first two entries in .got.plt are reserved.
+  elfcpp::Swap<size, big_endian>::writeval(gotplt_pov, 0);
+  elfcpp::Swap<size, big_endian>::writeval(gotplt_pov + got_entry_size, 0);
+
+  unsigned int gotplt_offset = 2 * got_entry_size;
+  gotplt_pov += 2 * got_entry_size;
+
+  // Calculate the address of the PLT header.
+  Mips_address header_address = (plt_address
+                                 + (this->is_plt_header_compressed() ? 1 : 0));
+
+  // Initialize compressed PLT area view.
+  unsigned char* pov2 = pov + this->plt_mips_offset_;
+
+  // Write the PLT entries.
+  for (typename std::vector<Mips_symbol<size>*>::const_iterator
+       p = this->symbols_.begin();
+       p != this->symbols_.end();
+       ++p, gotplt_pov += got_entry_size, gotplt_offset += got_entry_size)
+    {
+      Mips_symbol<size>* mips_sym = *p;
+
+      // Calculate the address of the .got.plt entry.
+      uint32_t gotplt_entry_addr = (gotplt_addr + gotplt_offset);
+      uint32_t gotplt_entry_addr_hi = (((gotplt_entry_addr + 0x8000) >> 16)
+                                       & 0xffff);
+      uint32_t gotplt_entry_addr_lo = gotplt_entry_addr & 0xffff;
+
+      // Initially point the .got.plt entry at the PLT header.
+      if (this->target_->is_output_n64())
+        elfcpp::Swap<64, big_endian>::writeval(gotplt_pov, header_address);
+      else
+        elfcpp::Swap<32, big_endian>::writeval(gotplt_pov, header_address);
+
+      // Now handle the PLT itself.  First the standard entry.
+      if (mips_sym->has_mips_plt_offset())
+        {
+          // Pick the load opcode (LW or LD).
+          uint64_t load = this->target_->is_output_n64() ? 0xdc000000
+                                                         : 0x8c000000;
+
+          // Fill in the PLT entry itself.
+          elfcpp::Swap<32, big_endian>::writeval(pov,
+              plt_entry[0] | gotplt_entry_addr_hi);
+          elfcpp::Swap<32, big_endian>::writeval(pov + 4,
+              plt_entry[1] | gotplt_entry_addr_lo | load);
+          elfcpp::Swap<32, big_endian>::writeval(pov + 8, plt_entry[2]);
+          elfcpp::Swap<32, big_endian>::writeval(pov + 12,
+              plt_entry[3] | gotplt_entry_addr_lo);
+          pov += 16;
+        }
+
+      // Now the compressed entry.  They come after any standard ones.
+      if (mips_sym->has_comp_plt_offset())
+        {
+          if (!this->target_->is_output_micromips())
+            {
+              // Write MIPS16 PLT entry.
+              const uint32_t* plt_entry = plt_entry_mips16_o32;
+
+              elfcpp::Swap<16, big_endian>::writeval(pov2, plt_entry[0]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 2, plt_entry[1]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 4, plt_entry[2]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 6, plt_entry[3]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 8, plt_entry[4]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 10, plt_entry[5]);
+              elfcpp::Swap<32, big_endian>::writeval(pov2 + 12,
+                                                     gotplt_entry_addr);
+              pov2 += 16;
+            }
+          else if (this->target_->use_32bit_micromips_instructions())
+            {
+              // Write microMIPS PLT entry in insn32 mode.
+              const uint32_t* plt_entry = plt_entry_micromips32_o32;
+
+              elfcpp::Swap<16, big_endian>::writeval(pov2, plt_entry[0]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 2,
+                                                     gotplt_entry_addr_hi);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 4, plt_entry[2]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 6,
+                                                     gotplt_entry_addr_lo);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 8, plt_entry[4]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 10, plt_entry[5]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 12, plt_entry[6]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 14,
+                                                     gotplt_entry_addr_lo);
+              pov2 += 16;
+            }
+          else
+            {
+              // Write microMIPS PLT entry.
+              const uint32_t* plt_entry = plt_entry_micromips_o32;
+
+              gold_assert(gotplt_entry_addr % 4 == 0);
+
+              Mips_address loc_address = plt_address + pov2 - oview;
+              int gotpc_offset = gotplt_entry_addr - ((loc_address | 3) ^ 3);
+
+              // ADDIUPC has a span of +/-16MB, check we're in range.
+              if (gotpc_offset + 0x1000000 >= 0x2000000)
+                {
+                  gold_error(_(".got.plt offset of %ld from .plt beyond the "
+                             "range of ADDIUPC"), (long)gotpc_offset);
+                  return;
+                }
+
+              elfcpp::Swap<16, big_endian>::writeval(pov2,
+                          plt_entry[0] | ((gotpc_offset >> 18) & 0x7f));
+              elfcpp::Swap<16, big_endian>::writeval(
+                  pov2 + 2, (gotpc_offset >> 2) & 0xffff);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 4, plt_entry[2]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 6, plt_entry[3]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 8, plt_entry[4]);
+              elfcpp::Swap<16, big_endian>::writeval(pov2 + 10, plt_entry[5]);
+              pov2 += 12;
+            }
+        }
+    }
+
+  // Check the number of bytes written for standard entries.
+  gold_assert(static_cast<section_size_type>(
+      pov - oview - this->plt_header_size_) == this->plt_mips_offset_);
+  // Check the number of bytes written for compressed entries.
+  gold_assert((static_cast<section_size_type>(pov2 - pov)
+               == this->plt_comp_offset_));
+  // Check the total number of bytes written.
+  gold_assert(static_cast<section_size_type>(pov2 - oview) == oview_size);
+
+  gold_assert(static_cast<section_size_type>(gotplt_pov - gotplt_view)
+              == gotplt_size);
+
+  of->write_output_view(offset, oview_size, oview);
+  of->write_output_view(gotplt_file_offset, gotplt_size, gotplt_view);
+}
+
+// Mips_output_data_mips_stubs methods.
+
+// The format of the lazy binding stub when dynamic symbol count is less than
+// 64K, dynamic symbol index is less than 32K, and ABI is not N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_normal_1[4] =
+{
+  0x8f998010,         // lw t9,0x8010(gp)
+  0x03e07821,         // addu t7,ra,zero
+  0x0320f809,         // jalr t9,ra
+  0x24180000          // addiu t8,zero,DYN_INDEX sign extended
+};
+
+// The format of the lazy binding stub when dynamic symbol count is less than
+// 64K, dynamic symbol index is less than 32K, and ABI is N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_normal_1_n64[4] =
+{
+  0xdf998010,         // ld t9,0x8010(gp)
+  0x03e0782d,         // daddu t7,ra,zero
+  0x0320f809,         // jalr t9,ra
+  0x64180000          // daddiu t8,zero,DYN_INDEX sign extended
+};
+
+// The format of the lazy binding stub when dynamic symbol count is less than
+// 64K, dynamic symbol index is between 32K and 64K, and ABI is not N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_normal_2[4] =
+{
+  0x8f998010,         // lw t9,0x8010(gp)
+  0x03e07821,         // addu t7,ra,zero
+  0x0320f809,         // jalr t9,ra
+  0x34180000          // ori t8,zero,DYN_INDEX unsigned
+};
+
+// The format of the lazy binding stub when dynamic symbol count is less than
+// 64K, dynamic symbol index is between 32K and 64K, and ABI is N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_normal_2_n64[4] =
+{
+  0xdf998010,         // ld t9,0x8010(gp)
+  0x03e0782d,         // daddu t7,ra,zero
+  0x0320f809,         // jalr t9,ra
+  0x34180000          // ori t8,zero,DYN_INDEX unsigned
+};
+
+// The format of the lazy binding stub when dynamic symbol count is greater than
+// 64K, and ABI is not N64.
+template<int size, bool big_endian>
+const uint32_t Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_big[5] =
+{
+  0x8f998010,         // lw t9,0x8010(gp)
+  0x03e07821,         // addu t7,ra,zero
+  0x3c180000,         // lui t8,DYN_INDEX
+  0x0320f809,         // jalr t9,ra
+  0x37180000          // ori t8,t8,DYN_INDEX
+};
+
+// The format of the lazy binding stub when dynamic symbol count is greater than
+// 64K, and ABI is N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_big_n64[5] =
+{
+  0xdf998010,         // ld t9,0x8010(gp)
+  0x03e0782d,         // daddu t7,ra,zero
+  0x3c180000,         // lui t8,DYN_INDEX
+  0x0320f809,         // jalr t9,ra
+  0x37180000          // ori t8,t8,DYN_INDEX
+};
+
+// microMIPS stubs.
+
+// The format of the microMIPS lazy binding stub when dynamic symbol count is
+// less than 64K, dynamic symbol index is less than 32K, and ABI is not N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_micromips_normal_1[] =
+{
+  0xff3c, 0x8010,     // lw t9,0x8010(gp)
+  0x0dff,             // move t7,ra
+  0x45d9,             // jalr t9
+  0x3300, 0x0000      // addiu t8,zero,DYN_INDEX sign extended
+};
+
+// The format of the microMIPS lazy binding stub when dynamic symbol count is
+// less than 64K, dynamic symbol index is less than 32K, and ABI is N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::
+lazy_stub_micromips_normal_1_n64[] =
+{
+  0xdf3c, 0x8010,     // ld t9,0x8010(gp)
+  0x0dff,             // move t7,ra
+  0x45d9,             // jalr t9
+  0x5f00, 0x0000      // daddiu t8,zero,DYN_INDEX sign extended
+};
+
+// The format of the microMIPS lazy binding stub when dynamic symbol
+// count is less than 64K, dynamic symbol index is between 32K and 64K,
+// and ABI is not N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_micromips_normal_2[] =
+{
+  0xff3c, 0x8010,     // lw t9,0x8010(gp)
+  0x0dff,             // move t7,ra
+  0x45d9,             // jalr t9
+  0x5300, 0x0000      // ori t8,zero,DYN_INDEX unsigned
+};
+
+// The format of the microMIPS lazy binding stub when dynamic symbol
+// count is less than 64K, dynamic symbol index is between 32K and 64K,
+// and ABI is N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::
+lazy_stub_micromips_normal_2_n64[] =
+{
+  0xdf3c, 0x8010,     // ld t9,0x8010(gp)
+  0x0dff,             // move t7,ra
+  0x45d9,             // jalr t9
+  0x5300, 0x0000      // ori t8,zero,DYN_INDEX unsigned
+};
+
+// The format of the microMIPS lazy binding stub when dynamic symbol count is
+// greater than 64K, and ABI is not N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_micromips_big[] =
+{
+  0xff3c, 0x8010,     // lw t9,0x8010(gp)
+  0x0dff,             // move t7,ra
+  0x41b8, 0x0000,     // lui t8,DYN_INDEX
+  0x45d9,             // jalr t9
+  0x5318, 0x0000      // ori t8,t8,DYN_INDEX
+};
+
+// The format of the microMIPS lazy binding stub when dynamic symbol count is
+// greater than 64K, and ABI is N64.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_micromips_big_n64[] =
+{
+  0xdf3c, 0x8010,     // ld t9,0x8010(gp)
+  0x0dff,             // move t7,ra
+  0x41b8, 0x0000,     // lui t8,DYN_INDEX
+  0x45d9,             // jalr t9
+  0x5318, 0x0000      // ori t8,t8,DYN_INDEX
+};
+
+// 32-bit microMIPS stubs.
+
+// The format of the microMIPS lazy binding stub when dynamic symbol count is
+// less than 64K, dynamic symbol index is less than 32K, ABI is not N64, and we
+// can use only 32-bit instructions.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::
+lazy_stub_micromips32_normal_1[] =
+{
+  0xff3c, 0x8010,     // lw t9,0x8010(gp)
+  0x001f, 0x7950,     // addu t7,ra,zero
+  0x03f9, 0x0f3c,     // jalr ra,t9
+  0x3300, 0x0000      // addiu t8,zero,DYN_INDEX sign extended
+};
+
+// The format of the microMIPS lazy binding stub when dynamic symbol count is
+// less than 64K, dynamic symbol index is less than 32K, ABI is N64, and we can
+// use only 32-bit instructions.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::
+lazy_stub_micromips32_normal_1_n64[] =
+{
+  0xdf3c, 0x8010,     // ld t9,0x8010(gp)
+  0x581f, 0x7950,     // daddu t7,ra,zero
+  0x03f9, 0x0f3c,     // jalr ra,t9
+  0x5f00, 0x0000      // daddiu t8,zero,DYN_INDEX sign extended
+};
+
+// The format of the microMIPS lazy binding stub when dynamic symbol
+// count is less than 64K, dynamic symbol index is between 32K and 64K,
+// ABI is not N64, and we can use only 32-bit instructions.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::
+lazy_stub_micromips32_normal_2[] =
+{
+  0xff3c, 0x8010,     // lw t9,0x8010(gp)
+  0x001f, 0x7950,     // addu t7,ra,zero
+  0x03f9, 0x0f3c,     // jalr ra,t9
+  0x5300, 0x0000      // ori t8,zero,DYN_INDEX unsigned
+};
+
+// The format of the microMIPS lazy binding stub when dynamic symbol
+// count is less than 64K, dynamic symbol index is between 32K and 64K,
+// ABI is N64, and we can use only 32-bit instructions.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::
+lazy_stub_micromips32_normal_2_n64[] =
+{
+  0xdf3c, 0x8010,     // ld t9,0x8010(gp)
+  0x581f, 0x7950,     // daddu t7,ra,zero
+  0x03f9, 0x0f3c,     // jalr ra,t9
+  0x5300, 0x0000      // ori t8,zero,DYN_INDEX unsigned
+};
+
+// The format of the microMIPS lazy binding stub when dynamic symbol count is
+// greater than 64K, ABI is not N64, and we can use only 32-bit instructions.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_micromips32_big[] =
+{
+  0xff3c, 0x8010,     // lw t9,0x8010(gp)
+  0x001f, 0x7950,     // addu t7,ra,zero
+  0x41b8, 0x0000,     // lui t8,DYN_INDEX
+  0x03f9, 0x0f3c,     // jalr ra,t9
+  0x5318, 0x0000      // ori t8,t8,DYN_INDEX
+};
+
+// The format of the microMIPS lazy binding stub when dynamic symbol count is
+// greater than 64K, ABI is N64, and we can use only 32-bit instructions.
+template<int size, bool big_endian>
+const uint32_t
+Mips_output_data_mips_stubs<size, big_endian>::lazy_stub_micromips32_big_n64[] =
+{
+  0xdf3c, 0x8010,     // ld t9,0x8010(gp)
+  0x581f, 0x7950,     // daddu t7,ra,zero
+  0x41b8, 0x0000,     // lui t8,DYN_INDEX
+  0x03f9, 0x0f3c,     // jalr ra,t9
+  0x5318, 0x0000      // ori t8,t8,DYN_INDEX
+};
+
+// Create entry for a symbol.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_mips_stubs<size, big_endian>::make_entry(
+    Mips_symbol<size>* gsym)
+{
+  if (!gsym->has_lazy_stub() && !gsym->has_plt_offset())
+    {
+      this->symbols_.insert(gsym);
+      gsym->set_has_lazy_stub(true);
+    }
+}
+
+// Remove entry for a symbol.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_mips_stubs<size, big_endian>::remove_entry(
+    Mips_symbol<size>* gsym)
+{
+  if (gsym->has_lazy_stub())
+    {
+      this->symbols_.erase(gsym);
+      gsym->set_has_lazy_stub(false);
+    }
+}
+
+// Set stub offsets for symbols.  This method expects that the number of
+// entries in dynamic symbol table is set.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_mips_stubs<size, big_endian>::set_lazy_stub_offsets()
+{
+  gold_assert(this->dynsym_count_ != -1U);
+
+  if (this->stub_offsets_are_set_)
+    return;
+
+  unsigned int stub_size = this->stub_size();
+  unsigned int offset = 0;
+  for (typename Unordered_set<Mips_symbol<size>*>::const_iterator
+       p = this->symbols_.begin();
+       p != this->symbols_.end();
+       ++p, offset += stub_size)
+    {
+      Mips_symbol<size>* mips_sym = *p;
+      mips_sym->set_lazy_stub_offset(offset);
+    }
+  this->stub_offsets_are_set_ = true;
+}
+
+template<int size, bool big_endian>
+void
+Mips_output_data_mips_stubs<size, big_endian>::set_needs_dynsym_value()
+{
+  for (typename Unordered_set<Mips_symbol<size>*>::const_iterator
+       p = this->symbols_.begin(); p != this->symbols_.end(); ++p)
+    {
+      Mips_symbol<size>* sym = *p;
+      if (sym->is_from_dynobj())
+        sym->set_needs_dynsym_value();
+    }
+}
+
+// Write out the .MIPS.stubs.  This uses the hand-coded instructions and
+// adjusts them as needed.
+
+template<int size, bool big_endian>
+void
+Mips_output_data_mips_stubs<size, big_endian>::do_write(Output_file* of)
+{
+  const off_t offset = this->offset();
+  const section_size_type oview_size =
+    convert_to_section_size_type(this->data_size());
+  unsigned char* const oview = of->get_output_view(offset, oview_size);
+
+  bool big_stub = this->dynsym_count_ > 0x10000;
+
+  unsigned char* pov = oview;
+  for (typename Unordered_set<Mips_symbol<size>*>::const_iterator
+       p = this->symbols_.begin(); p != this->symbols_.end(); ++p)
+    {
+      Mips_symbol<size>* sym = *p;
+      const uint32_t* lazy_stub;
+      bool n64 = this->target_->is_output_n64();
+
+      if (!this->target_->is_output_micromips())
+        {
+          // Write standard (non-microMIPS) stub.
+          if (!big_stub)
+            {
+              if (sym->dynsym_index() & ~0x7fff)
+                // Dynsym index is between 32K and 64K.
+                lazy_stub = n64 ? lazy_stub_normal_2_n64 : lazy_stub_normal_2;
+              else
+                // Dynsym index is less than 32K.
+                lazy_stub = n64 ? lazy_stub_normal_1_n64 : lazy_stub_normal_1;
+            }
+          else
+            lazy_stub = n64 ? lazy_stub_big_n64 : lazy_stub_big;
+
+          unsigned int i = 0;
+          elfcpp::Swap<32, big_endian>::writeval(pov, lazy_stub[i]);
+          elfcpp::Swap<32, big_endian>::writeval(pov + 4, lazy_stub[i + 1]);
+          pov += 8;
+
+          i += 2;
+          if (big_stub)
+            {
+              // LUI instruction of the big stub.  Paste high 16 bits of the
+              // dynsym index.
+              elfcpp::Swap<32, big_endian>::writeval(pov,
+                  lazy_stub[i] | ((sym->dynsym_index() >> 16) & 0x7fff));
+              pov += 4;
+              i += 1;
+            }
+          elfcpp::Swap<32, big_endian>::writeval(pov, lazy_stub[i]);
+          // Last stub instruction.  Paste low 16 bits of the dynsym index.
+          elfcpp::Swap<32, big_endian>::writeval(pov + 4,
+              lazy_stub[i + 1] | (sym->dynsym_index() & 0xffff));
+          pov += 8;
+        }
+      else if (this->target_->use_32bit_micromips_instructions())
+        {
+          // Write microMIPS stub in insn32 mode.
+          if (!big_stub)
+            {
+              if (sym->dynsym_index() & ~0x7fff)
+                // Dynsym index is between 32K and 64K.
+                lazy_stub = n64 ? lazy_stub_micromips32_normal_2_n64
+                                : lazy_stub_micromips32_normal_2;
+              else
+                // Dynsym index is less than 32K.
+                lazy_stub = n64 ? lazy_stub_micromips32_normal_1_n64
+                                : lazy_stub_micromips32_normal_1;
+            }
+          else
+            lazy_stub = n64 ? lazy_stub_micromips32_big_n64
+                            : lazy_stub_micromips32_big;
+
+          unsigned int i = 0;
+          // First stub instruction.  We emit 32-bit microMIPS instructions by
+          // emitting two 16-bit parts because on microMIPS the 16-bit part of
+          // the instruction where the opcode is must always come first, for
+          // both little and big endian.
+          elfcpp::Swap<16, big_endian>::writeval(pov, lazy_stub[i]);
+          elfcpp::Swap<16, big_endian>::writeval(pov + 2, lazy_stub[i + 1]);
+          // Second stub instruction.
+          elfcpp::Swap<16, big_endian>::writeval(pov + 4, lazy_stub[i + 2]);
+          elfcpp::Swap<16, big_endian>::writeval(pov + 6, lazy_stub[i + 3]);
+          pov += 8;
+          i += 4;
+          if (big_stub)
+            {
+              // LUI instruction of the big stub.  Paste high 16 bits of the
+              // dynsym index.
+              elfcpp::Swap<16, big_endian>::writeval(pov, lazy_stub[i]);
+              elfcpp::Swap<16, big_endian>::writeval(pov + 2,
+                  (sym->dynsym_index() >> 16) & 0x7fff);
+              pov += 4;
+              i += 2;
+            }
+          elfcpp::Swap<16, big_endian>::writeval(pov, lazy_stub[i]);
+          elfcpp::Swap<16, big_endian>::writeval(pov + 2, lazy_stub[i + 1]);
+          // Last stub instruction.  Paste low 16 bits of the dynsym index.
+          elfcpp::Swap<16, big_endian>::writeval(pov + 4, lazy_stub[i + 2]);
+          elfcpp::Swap<16, big_endian>::writeval(pov + 6,
+              sym->dynsym_index() & 0xffff);
+          pov += 8;
+        }
+      else
+        {
+          // Write microMIPS stub.
+          if (!big_stub)
+            {
+              if (sym->dynsym_index() & ~0x7fff)
+                // Dynsym index is between 32K and 64K.
+                lazy_stub = n64 ? lazy_stub_micromips_normal_2_n64
+                                : lazy_stub_micromips_normal_2;
+              else
+                // Dynsym index is less than 32K.
+                lazy_stub = n64 ? lazy_stub_micromips_normal_1_n64
+                                : lazy_stub_micromips_normal_1;
+            }
+          else
+            lazy_stub = n64 ? lazy_stub_micromips_big_n64
+                            : lazy_stub_micromips_big;
+
+          unsigned int i = 0;
+          // First stub instruction.  We emit 32-bit microMIPS instructions by
+          // emitting two 16-bit parts because on microMIPS the 16-bit part of
+          // the instruction where the opcode is must always come first, for
+          // both little and big endian.
+          elfcpp::Swap<16, big_endian>::writeval(pov, lazy_stub[i]);
+          elfcpp::Swap<16, big_endian>::writeval(pov + 2, lazy_stub[i + 1]);
+          // Second stub instruction.
+          elfcpp::Swap<16, big_endian>::writeval(pov + 4, lazy_stub[i + 2]);
+          pov += 6;
+          i += 3;
+          if (big_stub)
+            {
+              // LUI instruction of the big stub.  Paste high 16 bits of the
+              // dynsym index.
+              elfcpp::Swap<16, big_endian>::writeval(pov, lazy_stub[i]);
+              elfcpp::Swap<16, big_endian>::writeval(pov + 2,
+                  (sym->dynsym_index() >> 16) & 0x7fff);
+              pov += 4;
+              i += 2;
+            }
+          elfcpp::Swap<16, big_endian>::writeval(pov, lazy_stub[i]);
+          // Last stub instruction.  Paste low 16 bits of the dynsym index.
+          elfcpp::Swap<16, big_endian>::writeval(pov + 2, lazy_stub[i + 1]);
+          elfcpp::Swap<16, big_endian>::writeval(pov + 4,
+              sym->dynsym_index() & 0xffff);
+          pov += 6;
+        }
+    }
+
+  // We always allocate 20 bytes for every stub, because final dynsym count is
+  // not known in method do_finalize_sections.  There are 4 unused bytes per
+  // stub if final dynsym count is less than 0x10000.
+  unsigned int used = pov - oview;
+  unsigned int unused = big_stub ? 0 : this->symbols_.size() * 4;
+  gold_assert(static_cast<section_size_type>(used + unused) == oview_size);
+
+  // Fill the unused space with zeroes.
+  // TODO(sasa): Can we strip unused bytes during the relaxation?
+  if (unused > 0)
+    memset(pov, 0, unused);
+
+  of->write_output_view(offset, oview_size, oview);
+}
+
+// Mips_output_section_reginfo methods.
+
+template<int size, bool big_endian>
+void
+Mips_output_section_reginfo<size, big_endian>::do_write(Output_file* of)
+{
+  off_t offset = this->offset();
+  off_t data_size = this->data_size();
+
+  unsigned char* view = of->get_output_view(offset, data_size);
+  elfcpp::Swap<size, big_endian>::writeval(view, this->gprmask_);
+  elfcpp::Swap<size, big_endian>::writeval(view + 4, this->cprmask1_);
+  elfcpp::Swap<size, big_endian>::writeval(view + 8, this->cprmask2_);
+  elfcpp::Swap<size, big_endian>::writeval(view + 12, this->cprmask3_);
+  elfcpp::Swap<size, big_endian>::writeval(view + 16, this->cprmask4_);
+  // Write the gp value.
+  elfcpp::Swap<size, big_endian>::writeval(view + 20,
+                                           this->target_->gp_value());
+
+  of->write_output_view(offset, data_size, view);
+}
+
+// Mips_copy_relocs methods.
+
+// Emit any saved relocs.
+
+template<int sh_type, int size, bool big_endian>
+void
+Mips_copy_relocs<sh_type, size, big_endian>::emit_mips(
+    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_relocs<sh_type, size, big_endian>::
+       Copy_reloc_entries::iterator p = this->entries_.begin();
+       p != this->entries_.end();
+       ++p)
+    emit_entry(*p, reloc_section, symtab, layout, target);
+
+  // We no longer need the saved information.
+  this->entries_.clear();
+}
+
+// Emit the reloc if appropriate.
+
+template<int sh_type, int size, bool big_endian>
+void
+Mips_copy_relocs<sh_type, size, big_endian>::emit_entry(
+    Copy_reloc_entry& entry,
+    Output_data_reloc<sh_type, true, size, big_endian>* reloc_section,
+    Symbol_table* symtab, Layout* layout, Target_mips<size, big_endian>* target)
+{
+  // 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 (!entry.sym_->is_from_dynobj())
+    return;
+
+  bool can_make_dynamic = (entry.reloc_type_ == elfcpp::R_MIPS_32
+                           || entry.reloc_type_ == elfcpp::R_MIPS_REL32
+                           || entry.reloc_type_ == elfcpp::R_MIPS_64);
+
+  Mips_symbol<size>* sym = Mips_symbol<size>::as_mips_sym(entry.sym_);
+  if (can_make_dynamic && !sym->has_static_relocs())
+    {
+      Mips_relobj<size, big_endian>* object =
+        Mips_relobj<size, big_endian>::as_mips_relobj(entry.relobj_);
+      target->got_section(symtab, layout)->record_global_got_symbol(
+                          sym, object, entry.reloc_type_, true, false);
+      if (!symbol_references_local(sym, sym->should_add_dynsym_entry(symtab)))
+        target->rel_dyn_section(layout)->add_global(sym, elfcpp::R_MIPS_REL32,
+            entry.output_section_, entry.relobj_, entry.shndx_, entry.address_);
+      else
+        target->rel_dyn_section(layout)->add_symbolless_global_addend(
+            sym, elfcpp::R_MIPS_REL32, entry.output_section_, entry.relobj_,
+            entry.shndx_, entry.address_);
+    }
+  else
+    this->make_copy_reloc(symtab, layout,
+                          static_cast<Sized_symbol<size>*>(entry.sym_),
+                          reloc_section);
+}
+
+// Target_mips methods.
+
+// 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.
+
+template<int size, bool big_endian>
+uint64_t
+Target_mips<size, big_endian>::do_dynsym_value(const Symbol* gsym) const
+{
+  uint64_t value = 0;
+  const Mips_symbol<size>* mips_sym = Mips_symbol<size>::as_mips_sym(gsym);
+
+  if (!mips_sym->has_lazy_stub())
+    {
+      if (mips_sym->has_plt_offset())
+        {
+          // We distinguish between PLT entries and lazy-binding stubs by
+          // giving the former an st_other value of STO_MIPS_PLT.  Set the
+          // value to the stub address if there are any relocations in the
+          // binary where pointer equality matters.
+          if (mips_sym->pointer_equality_needed())
+            {
+              // Prefer a standard MIPS PLT entry.
+              if (mips_sym->has_mips_plt_offset())
+                value = this->plt_section()->mips_entry_address(mips_sym);
+              else
+                value = this->plt_section()->comp_entry_address(mips_sym) + 1;
+            }
+          else
+            value = 0;
+        }
+    }
+  else
+    {
+      // First, set stub offsets for symbols.  This method expects that the
+      // number of entries in dynamic symbol table is set.
+      this->mips_stubs_section()->set_lazy_stub_offsets();
+
+      // The run-time linker uses the st_value field of the symbol
+      // to reset the global offset table entry for this external
+      // to its stub address when unlinking a shared object.
+      value = this->mips_stubs_section()->stub_address(mips_sym);
+    }
+
+  if (mips_sym->has_mips16_fn_stub())
+    {
+      // If we have a MIPS16 function with a stub, the dynamic symbol must
+      // refer to the stub, since only the stub uses the standard calling
+      // conventions.
+      value = mips_sym->template
+              get_mips16_fn_stub<big_endian>()->output_address();
+    }
+
+  return value;
+}
+
+// Get the dynamic reloc section, creating it if necessary.  It's always
+// .rel.dyn, even for MIPS64.
+
+template<int size, bool big_endian>
+typename Target_mips<size, big_endian>::Reloc_section*
+Target_mips<size, big_endian>::rel_dyn_section(Layout* layout)
+{
+  if (this->rel_dyn_ == NULL)
+    {
+      gold_assert(layout != NULL);
+      this->rel_dyn_ = new Reloc_section(parameters->options().combreloc());
+      layout->add_output_section_data(".rel.dyn", elfcpp::SHT_REL,
+                                      elfcpp::SHF_ALLOC, this->rel_dyn_,
+                                      ORDER_DYNAMIC_RELOCS, false);
+
+      // First entry in .rel.dyn has to be null.
+      // This is hack - we define dummy output data and set its address to 0,
+      // and define absolute R_MIPS_NONE relocation with offset 0 against it.
+      // This ensures that the entry is null.
+      Output_data* od = new Output_data_zero_fill(0, 0);
+      od->set_address(0);
+      this->rel_dyn_->add_absolute(elfcpp::R_MIPS_NONE, od, 0);
+    }
+  return this->rel_dyn_;
+}
+
+// Get the GOT section, creating it if necessary.
+
+template<int size, bool big_endian>
+Mips_output_data_got<size, big_endian>*
+Target_mips<size, big_endian>::got_section(Symbol_table* symtab,
+                                           Layout* layout)
+{
+  if (this->got_ == NULL)
+    {
+      gold_assert(symtab != NULL && layout != NULL);
+
+      this->got_ = new Mips_output_data_got<size, big_endian>(this, symtab,
+                                                              layout);
+      layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
+                                      (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE |
+                                      elfcpp::SHF_MIPS_GPREL),
+                                      this->got_, ORDER_DATA, false);
+
+      // Define _GLOBAL_OFFSET_TABLE_ at the start of the .got section.
+      symtab->define_in_output_data("_GLOBAL_OFFSET_TABLE_", NULL,
+                                    Symbol_table::PREDEFINED,
+                                    this->got_,
+                                    0, 0, elfcpp::STT_OBJECT,
+                                    elfcpp::STB_GLOBAL,
+                                    elfcpp::STV_DEFAULT, 0,
+                                    false, false);
+    }
+
+  return this->got_;
+}
+
+// Calculate value of _gp symbol.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::set_gp(Layout* layout, Symbol_table* symtab)
+{
+  if (this->gp_ != NULL)
+    return;
+
+  Output_data* section = layout->find_output_section(".got");
+  if (section == NULL)
+    {
+      // If there is no .got section, gp should be based on .sdata.
+      // TODO(sasa): This is probably not needed.  This was needed for older
+      // MIPS architectures which accessed both GOT and .sdata section using
+      // gp-relative addressing.  Modern Mips Linux ELF architectures don't
+      // access .sdata using gp-relative addressing.
+      for (Layout::Section_list::const_iterator
+           p = layout->section_list().begin();
+           p != layout->section_list().end();
+           ++p)
+        {
+          if (strcmp((*p)->name(), ".sdata") == 0)
+            {
+              section = *p;
+              break;
+            }
+        }
+    }
+
+  Sized_symbol<size>* gp =
+    static_cast<Sized_symbol<size>*>(symtab->lookup("_gp"));
+  if (gp != NULL)
+    {
+      if (gp->source() != Symbol::IS_CONSTANT && section != NULL)
+        gp->init_output_data(gp->name(), NULL, section, MIPS_GP_OFFSET, 0,
+                             elfcpp::STT_OBJECT,
+                             elfcpp::STB_GLOBAL,
+                             elfcpp::STV_DEFAULT, 0,
+                             false, false);
+      this->gp_ = gp;
+    }
+  else if (section != NULL)
+    {
+      gp = static_cast<Sized_symbol<size>*>(symtab->define_in_output_data(
+                                      "_gp", NULL, Symbol_table::PREDEFINED,
+                                      section, MIPS_GP_OFFSET, 0,
+                                      elfcpp::STT_OBJECT,
+                                      elfcpp::STB_GLOBAL,
+                                      elfcpp::STV_DEFAULT,
+                                      0, false, false));
+      this->gp_ = gp;
+    }
+}
+
+// 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.
+
+template<int size, bool big_endian>
+unsigned int
+Target_mips<size, big_endian>::do_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<size, big_endian>(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_first_global_got_dynsym_index(
+    got_sym_count > 0 ? index - got_sym_count : -1U);
+
+  if (this->mips_stubs_ != NULL)
+    this->mips_stubs_->set_dynsym_count(index);
+
+  return index;
+}
+
+// Create a PLT entry for a global symbol referenced by r_type relocation.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::make_plt_entry(Symbol_table* symtab,
+                                              Layout* layout,
+                                              Mips_symbol<size>* gsym,
+                                              unsigned int r_type)
+{
+  if (gsym->has_lazy_stub() || gsym->has_plt_offset())
+    return;
+
+  if (this->plt_ == NULL)
+    {
+      // Create the GOT section first.
+      this->got_section(symtab, layout);
+
+      this->got_plt_ = new Output_data_space(4, "** GOT PLT");
+      layout->add_output_section_data(".got.plt", elfcpp::SHT_PROGBITS,
+                                      (elfcpp::SHF_ALLOC | elfcpp::SHF_WRITE),
+                                      this->got_plt_, ORDER_DATA, false);
+
+      // The first two entries are reserved.
+      this->got_plt_->set_current_data_size(2 * size/8);
+
+      this->plt_ = new Mips_output_data_plt<size, big_endian>(layout,
+                                                              this->got_plt_,
+                                                              this);
+      layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
+                                      (elfcpp::SHF_ALLOC
+                                       | elfcpp::SHF_EXECINSTR),
+                                      this->plt_, ORDER_PLT, false);
+    }
+
+  this->plt_->add_entry(gsym, r_type);
+}
+
+
+// Get the .MIPS.stubs section, creating it if necessary.
+
+template<int size, bool big_endian>
+Mips_output_data_mips_stubs<size, big_endian>*
+Target_mips<size, big_endian>::mips_stubs_section(Layout* layout)
+{
+  if (this->mips_stubs_ == NULL)
+    {
+      this->mips_stubs_ =
+        new Mips_output_data_mips_stubs<size, big_endian>(this);
+      layout->add_output_section_data(".MIPS.stubs", elfcpp::SHT_PROGBITS,
+                                      (elfcpp::SHF_ALLOC
+                                       | elfcpp::SHF_EXECINSTR),
+                                      this->mips_stubs_, ORDER_PLT, false);
+    }
+  return this->mips_stubs_;
+}
+
+// Get the LA25 stub section, creating it if necessary.
+
+template<int size, bool big_endian>
+Mips_output_data_la25_stub<size, big_endian>*
+Target_mips<size, big_endian>::la25_stub_section(Layout* layout)
+{
+  if (this->la25_stub_ == NULL)
+    {
+      this->la25_stub_ = new Mips_output_data_la25_stub<size, big_endian>();
+      layout->add_output_section_data(".text", elfcpp::SHT_PROGBITS,
+                                      (elfcpp::SHF_ALLOC
+                                       | elfcpp::SHF_EXECINSTR),
+                                      this->la25_stub_, ORDER_TEXT, false);
+    }
+  return this->la25_stub_;
+}
+
+// Process the relocations to determine unreferenced sections for
+// garbage collection.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::gc_process_relocs(
+                        Symbol_table* symtab,
+                        Layout* layout,
+                        Sized_relobj_file<size, big_endian>* object,
+                        unsigned int data_shndx,
+                        unsigned int,
+                        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)
+{
+  typedef Target_mips<size, big_endian> Mips;
+  typedef typename Target_mips<size, big_endian>::Scan Scan;
+
+  gold::gc_process_relocs<size, big_endian, Mips, elfcpp::SHT_REL, Scan,
+                          typename Target_mips::Relocatable_size_for_reloc>(
+    symtab,
+    layout,
+    this,
+    object,
+    data_shndx,
+    prelocs,
+    reloc_count,
+    output_section,
+    needs_special_offset_handling,
+    local_symbol_count,
+    plocal_symbols);
+}
+
+// Scan relocations for a section.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::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)
+{
+  typedef Target_mips<size, big_endian> Mips;
+  typedef typename Target_mips<size, big_endian>::Scan Scan;
+
+  if (sh_type == elfcpp::SHT_REL)
+    gold::scan_relocs<size, big_endian, Mips, elfcpp::SHT_REL, Scan>(
+      symtab,
+      layout,
+      this,
+      object,
+      data_shndx,
+      prelocs,
+      reloc_count,
+      output_section,
+      needs_special_offset_handling,
+      local_symbol_count,
+      plocal_symbols);
+  else if (sh_type == elfcpp::SHT_RELA)
+    gold::scan_relocs<size, big_endian, Mips, elfcpp::SHT_RELA, Scan>(
+      symtab,
+      layout,
+      this,
+      object,
+      data_shndx,
+      prelocs,
+      reloc_count,
+      output_section,
+      needs_special_offset_handling,
+      local_symbol_count,
+      plocal_symbols);
+}
+
+template<int size, bool big_endian>
+bool
+Target_mips<size, big_endian>::mips_32bit_flags(elfcpp::Elf_Word flags)
+{
+  return ((flags & elfcpp::EF_MIPS_32BITMODE) != 0
+          || (flags & elfcpp::EF_MIPS_ABI) == elfcpp::E_MIPS_ABI_O32
+          || (flags & elfcpp::EF_MIPS_ABI) == elfcpp::E_MIPS_ABI_EABI32
+          || (flags & elfcpp::EF_MIPS_ARCH) == elfcpp::E_MIPS_ARCH_1
+          || (flags & elfcpp::EF_MIPS_ARCH) == elfcpp::E_MIPS_ARCH_2
+          || (flags & elfcpp::EF_MIPS_ARCH) == elfcpp::E_MIPS_ARCH_32
+          || (flags & elfcpp::EF_MIPS_ARCH) == elfcpp::E_MIPS_ARCH_32R2);
+}
+
+// Return the MACH for a MIPS e_flags value.
+template<int size, bool big_endian>
+unsigned int
+Target_mips<size, big_endian>::elf_mips_mach(elfcpp::Elf_Word flags)
+{
+  switch (flags & elfcpp::EF_MIPS_MACH)
+    {
+    case elfcpp::E_MIPS_MACH_3900:
+      return mach_mips3900;
+
+    case elfcpp::E_MIPS_MACH_4010:
+      return mach_mips4010;
+
+    case elfcpp::E_MIPS_MACH_4100:
+      return mach_mips4100;
+
+    case elfcpp::E_MIPS_MACH_4111:
+      return mach_mips4111;
+
+    case elfcpp::E_MIPS_MACH_4120:
+      return mach_mips4120;
+
+    case elfcpp::E_MIPS_MACH_4650:
+      return mach_mips4650;
+
+    case elfcpp::E_MIPS_MACH_5400:
+      return mach_mips5400;
+
+    case elfcpp::E_MIPS_MACH_5500:
+      return mach_mips5500;
+
+    case elfcpp::E_MIPS_MACH_9000:
+      return mach_mips9000;
+
+    case elfcpp::E_MIPS_MACH_SB1:
+      return mach_mips_sb1;
+
+    case elfcpp::E_MIPS_MACH_LS2E:
+      return mach_mips_loongson_2e;
+
+    case elfcpp::E_MIPS_MACH_LS2F:
+      return mach_mips_loongson_2f;
+
+    case elfcpp::E_MIPS_MACH_LS3A:
+      return mach_mips_loongson_3a;
+
+    case elfcpp::E_MIPS_MACH_OCTEON2:
+      return mach_mips_octeon2;
+
+    case elfcpp::E_MIPS_MACH_OCTEON:
+      return mach_mips_octeon;
+
+    case elfcpp::E_MIPS_MACH_XLR:
+      return mach_mips_xlr;
+
+    default:
+      switch (flags & elfcpp::EF_MIPS_ARCH)
+        {
+        default:
+        case elfcpp::E_MIPS_ARCH_1:
+          return mach_mips3000;
+
+        case elfcpp::E_MIPS_ARCH_2:
+          return mach_mips6000;
+
+        case elfcpp::E_MIPS_ARCH_3:
+          return mach_mips4000;
+
+        case elfcpp::E_MIPS_ARCH_4:
+          return mach_mips8000;
+
+        case elfcpp::E_MIPS_ARCH_5:
+          return mach_mips5;
+
+        case elfcpp::E_MIPS_ARCH_32:
+          return mach_mipsisa32;
+
+        case elfcpp::E_MIPS_ARCH_64:
+          return mach_mipsisa64;
+
+        case elfcpp::E_MIPS_ARCH_32R2:
+          return mach_mipsisa32r2;
+
+        case elfcpp::E_MIPS_ARCH_64R2:
+          return mach_mipsisa64r2;
+        }
+    }
+
+  return 0;
+}
+
+// Check whether machine EXTENSION is an extension of machine BASE.
+template<int size, bool big_endian>
+bool
+Target_mips<size, big_endian>::mips_mach_extends(unsigned int base,
+                                                 unsigned int extension)
+{
+  if (extension == base)
+    return true;
+
+  if ((base == mach_mipsisa32)
+      && this->mips_mach_extends(mach_mipsisa64, extension))
+    return true;
+
+  if ((base == mach_mipsisa32r2)
+      && this->mips_mach_extends(mach_mipsisa64r2, extension))
+    return true;
+
+  for (unsigned int i = 0; i < this->mips_mach_extensions_.size(); ++i)
+    if (extension == this->mips_mach_extensions_[i].first)
+      {
+        extension = this->mips_mach_extensions_[i].second;
+        if (extension == base)
+          return true;
+      }
+
+  return false;
+}
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::merge_processor_specific_flags(
+    const std::string& name, elfcpp::Elf_Word in_flags,
+    unsigned char in_ei_class, bool dyn_obj)
+{
+  // If flags are not set yet, just copy them.
+  if (!this->are_processor_specific_flags_set())
+    {
+      this->set_processor_specific_flags(in_flags);
+      this->ei_class_ = in_ei_class;
+      this->mach_ = this->elf_mips_mach(in_flags);
+      return;
+    }
+
+  elfcpp::Elf_Word new_flags = in_flags;
+  elfcpp::Elf_Word old_flags = this->processor_specific_flags();
+  elfcpp::Elf_Word merged_flags = this->processor_specific_flags();
+  merged_flags |= new_flags & elfcpp::EF_MIPS_NOREORDER;
+
+  // Check flag compatibility.
+  new_flags &= ~elfcpp::EF_MIPS_NOREORDER;
+  old_flags &= ~elfcpp::EF_MIPS_NOREORDER;
+
+  // Some IRIX 6 BSD-compatibility objects have this bit set.  It
+  // doesn't seem to matter.
+  new_flags &= ~elfcpp::EF_MIPS_XGOT;
+  old_flags &= ~elfcpp::EF_MIPS_XGOT;
+
+  // MIPSpro generates ucode info in n64 objects.  Again, we should
+  // just be able to ignore this.
+  new_flags &= ~elfcpp::EF_MIPS_UCODE;
+  old_flags &= ~elfcpp::EF_MIPS_UCODE;
+
+  // DSOs should only be linked with CPIC code.
+  if (dyn_obj)
+    new_flags |= elfcpp::EF_MIPS_PIC | elfcpp::EF_MIPS_CPIC;
+
+  if (new_flags == old_flags)
+    {
+      this->set_processor_specific_flags(merged_flags);
+      return;
+    }
+
+  if (((new_flags & (elfcpp::EF_MIPS_PIC | elfcpp::EF_MIPS_CPIC)) != 0)
+      != ((old_flags & (elfcpp::EF_MIPS_PIC | elfcpp::EF_MIPS_CPIC)) != 0))
+    gold_warning(_("%s: linking abicalls files with non-abicalls files"),
+                 name.c_str());
+
+  if (new_flags & (elfcpp::EF_MIPS_PIC | elfcpp::EF_MIPS_CPIC))
+    merged_flags |= elfcpp::EF_MIPS_CPIC;
+  if (!(new_flags & elfcpp::EF_MIPS_PIC))
+    merged_flags &= ~elfcpp::EF_MIPS_PIC;
+
+  new_flags &= ~(elfcpp::EF_MIPS_PIC | elfcpp::EF_MIPS_CPIC);
+  old_flags &= ~(elfcpp::EF_MIPS_PIC | elfcpp::EF_MIPS_CPIC);
+
+  // Compare the ISAs.
+  if (mips_32bit_flags(old_flags) != mips_32bit_flags(new_flags))
+    gold_error(_("%s: linking 32-bit code with 64-bit code"), name.c_str());
+  else if (!this->mips_mach_extends(this->elf_mips_mach(in_flags), this->mach_))
+    {
+      // Output ISA isn't the same as, or an extension of, input ISA.
+      if (this->mips_mach_extends(this->mach_, this->elf_mips_mach(in_flags)))
+        {
+          // Copy the architecture info from input object to output.  Also copy
+          // the 32-bit flag (if set) so that we continue to recognise
+          // output as a 32-bit binary.
+          this->mach_ = this->elf_mips_mach(in_flags);
+          merged_flags &= ~(elfcpp::EF_MIPS_ARCH | elfcpp::EF_MIPS_MACH);
+          merged_flags |= (new_flags & (elfcpp::EF_MIPS_ARCH
+                           | elfcpp::EF_MIPS_MACH | elfcpp::EF_MIPS_32BITMODE));
+
+          // Copy across the ABI flags if output doesn't use them
+          // and if that was what caused us to treat input object as 32-bit.
+          if ((old_flags & elfcpp::EF_MIPS_ABI) == 0
+              && this->mips_32bit_flags(new_flags)
+              && !this->mips_32bit_flags(new_flags & ~elfcpp::EF_MIPS_ABI))
+            merged_flags |= new_flags & elfcpp::EF_MIPS_ABI;
+        }
+      else
+        // The ISAs aren't compatible.
+        gold_error(_("%s: linking %s module with previous %s modules"),
+                   name.c_str(), this->elf_mips_mach_name(in_flags),
+                   this->elf_mips_mach_name(merged_flags));
+    }
+
+  new_flags &= (~(elfcpp::EF_MIPS_ARCH | elfcpp::EF_MIPS_MACH
+                | elfcpp::EF_MIPS_32BITMODE));
+  old_flags &= (~(elfcpp::EF_MIPS_ARCH | elfcpp::EF_MIPS_MACH
+                | elfcpp::EF_MIPS_32BITMODE));
+
+  // Compare ABIs.  The 64-bit ABI does not use EF_MIPS_ABI. But, it does set
+  // EI_CLASS differently from any 32-bit ABI.
+  if ((new_flags & elfcpp::EF_MIPS_ABI) != (old_flags & elfcpp::EF_MIPS_ABI)
+      || (in_ei_class != this->ei_class_))
+    {
+      // Only error if both are set (to different values).
+      if (((new_flags & elfcpp::EF_MIPS_ABI)
+           && (old_flags & elfcpp::EF_MIPS_ABI))
+          || (in_ei_class != this->ei_class_))
+        gold_error(_("%s: ABI mismatch: linking %s module with "
+                     "previous %s modules"), name.c_str(),
+                   this->elf_mips_abi_name(in_flags, in_ei_class),
+                   this->elf_mips_abi_name(merged_flags, this->ei_class_));
+
+      new_flags &= ~elfcpp::EF_MIPS_ABI;
+      old_flags &= ~elfcpp::EF_MIPS_ABI;
+    }
+
+  // Compare ASEs.  Forbid linking MIPS16 and microMIPS ASE modules together
+  // and allow arbitrary mixing of the remaining ASEs (retain the union).
+  if ((new_flags & elfcpp::EF_MIPS_ARCH_ASE)
+      != (old_flags & elfcpp::EF_MIPS_ARCH_ASE))
+    {
+      int old_micro = old_flags & elfcpp::EF_MIPS_ARCH_ASE_MICROMIPS;
+      int new_micro = new_flags & elfcpp::EF_MIPS_ARCH_ASE_MICROMIPS;
+      int old_m16 = old_flags & elfcpp::EF_MIPS_ARCH_ASE_M16;
+      int new_m16 = new_flags & elfcpp::EF_MIPS_ARCH_ASE_M16;
+      int micro_mis = old_m16 && new_micro;
+      int m16_mis = old_micro && new_m16;
+
+      if (m16_mis || micro_mis)
+        gold_error(_("%s: ASE mismatch: linking %s module with "
+                     "previous %s modules"), name.c_str(),
+                   m16_mis ? "MIPS16" : "microMIPS",
+                   m16_mis ? "microMIPS" : "MIPS16");
+
+      merged_flags |= new_flags & elfcpp::EF_MIPS_ARCH_ASE;
+
+      new_flags &= ~ elfcpp::EF_MIPS_ARCH_ASE;
+      old_flags &= ~ elfcpp::EF_MIPS_ARCH_ASE;
+    }
+
+  // Warn about any other mismatches.
+  if (new_flags != old_flags)
+    gold_error(_("%s: uses different e_flags (0x%x) fields than previous "
+                 "modules (0x%x)"), name.c_str(), new_flags, old_flags);
+
+  this->set_processor_specific_flags(merged_flags);
+}
+
+// Adjust ELF file header.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::do_adjust_elf_header(
+    unsigned char* view,
+    int len)
+{
+  gold_assert(len == elfcpp::Elf_sizes<size>::ehdr_size);
+
+  elfcpp::Ehdr<size, big_endian> ehdr(view);
+  unsigned char e_ident[elfcpp::EI_NIDENT];
+  memcpy(e_ident, ehdr.get_e_ident(), elfcpp::EI_NIDENT);
+
+  e_ident[elfcpp::EI_CLASS] = this->ei_class_;
+
+  elfcpp::Ehdr_write<size, big_endian> oehdr(view);
+  oehdr.put_e_ident(e_ident);
+  if (this->entry_symbol_is_compressed_)
+    oehdr.put_e_entry(ehdr.get_e_entry() + 1);
+}
+
+// do_make_elf_object to override the same function in the base class.
+// We need to use a target-specific sub-class of
+// Sized_relobj_file<size, big_endian> to store Mips specific information.
+// Hence we need to have our own ELF object creation.
+
+template<int size, bool big_endian>
+Object*
+Target_mips<size, big_endian>::do_make_elf_object(
+    const std::string& name,
+    Input_file* input_file,
+    off_t offset, const elfcpp::Ehdr<size, big_endian>& ehdr)
+{
+  int et = ehdr.get_e_type();
+  // ET_EXEC files are valid input for --just-symbols/-R,
+  // and we treat them as relocatable objects.
+  if (et == elfcpp::ET_REL
+      || (et == elfcpp::ET_EXEC && input_file->just_symbols()))
+    {
+      Mips_relobj<size, big_endian>* obj =
+        new Mips_relobj<size, big_endian>(name, input_file, offset, ehdr);
+      obj->setup();
+      return obj;
+    }
+  else if (et == elfcpp::ET_DYN)
+    {
+      // TODO(sasa): Should we create Mips_dynobj?
+      return Target::do_make_elf_object(name, input_file, offset, ehdr);
+    }
+  else
+    {
+      gold_error(_("%s: unsupported ELF file type %d"),
+                 name.c_str(), et);
+      return NULL;
+    }
+}
+
+// Finalize the sections.
+
+template <int size, bool big_endian>
+void
+Target_mips<size, big_endian>::do_finalize_sections(Layout* layout,
+                                        const Input_objects* input_objects,
+                                        Symbol_table* symtab)
+{
+  // Add +1 to MIPS16 and microMIPS init_ and _fini symbols so that DT_INIT and
+  // DT_FINI have correct values.
+  Mips_symbol<size>* init = static_cast<Mips_symbol<size>*>(
+      symtab->lookup(parameters->options().init()));
+  if (init != NULL && (init->is_mips16() || init->is_micromips()))
+    init->set_value(init->value() | 1);
+  Mips_symbol<size>* fini = static_cast<Mips_symbol<size>*>(
+      symtab->lookup(parameters->options().fini()));
+  if (fini != NULL && (fini->is_mips16() || fini->is_micromips()))
+    fini->set_value(fini->value() | 1);
+
+  // Check whether the entry symbol is mips16 or micromips.  This is needed to
+  // adjust entry address in ELF header.
+  Mips_symbol<size>* entry =
+    static_cast<Mips_symbol<size>*>(symtab->lookup(this->entry_symbol_name()));
+  this->entry_symbol_is_compressed_ = (entry != NULL && (entry->is_mips16()
+                                       || entry->is_micromips()));
+
+  if (!parameters->doing_static_link()
+      && (strcmp(parameters->options().hash_style(), "gnu") == 0
+          || strcmp(parameters->options().hash_style(), "both") == 0))
+    {
+      // .gnu.hash and the MIPS ABI require .dynsym to be sorted in different
+      // ways.  .gnu.hash needs symbols to be grouped by hash code whereas the
+      // MIPS ABI requires a mapping between the GOT and the symbol table.
+      gold_error(".gnu.hash is incompatible with the MIPS ABI");
+    }
+
+  // Check whether the final section that was scanned has HI16 or GOT16
+  // relocations without the corresponding LO16 part.
+  if (this->got16_addends_.size() > 0)
+      gold_error("Can't find matching LO16 reloc");
+
+  // Set _gp value.
+  this->set_gp(layout, symtab);
+
+  // Check for any mips16 stub sections that we can discard.
+  if (!parameters->options().relocatable())
+    {
+      for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
+          p != input_objects->relobj_end();
+          ++p)
+        {
+          Mips_relobj<size, big_endian>* object =
+            Mips_relobj<size, big_endian>::as_mips_relobj(*p);
+          object->discard_mips16_stub_sections(symtab);
+        }
+    }
+
+  // Merge processor-specific flags.
+  for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
+       p != input_objects->relobj_end();
+       ++p)
+    {
+      Mips_relobj<size, big_endian>* relobj =
+        Mips_relobj<size, big_endian>::as_mips_relobj(*p);
+
+      Input_file::Format format = relobj->input_file()->format();
+      if (format == Input_file::FORMAT_ELF)
+        {
+          // Read processor-specific flags in ELF file header.
+          const unsigned char* pehdr = relobj->get_view(
+                                            elfcpp::file_header_offset,
+                                            elfcpp::Elf_sizes<size>::ehdr_size,
+                                            true, false);
+
+          elfcpp::Ehdr<size, big_endian> ehdr(pehdr);
+          elfcpp::Elf_Word in_flags = ehdr.get_e_flags();
+          unsigned char ei_class = ehdr.get_e_ident()[elfcpp::EI_CLASS];
+
+          this->merge_processor_specific_flags(relobj->name(), in_flags,
+                                               ei_class, false);
+        }
+    }
+
+  for (Input_objects::Dynobj_iterator p = input_objects->dynobj_begin();
+       p != input_objects->dynobj_end();
+       ++p)
+    {
+      Sized_dynobj<size, big_endian>* dynobj =
+        static_cast<Sized_dynobj<size, big_endian>*>(*p);
+
+      // Read processor-specific flags.
+      const unsigned char* pehdr = dynobj->get_view(elfcpp::file_header_offset,
+                                           elfcpp::Elf_sizes<size>::ehdr_size,
+                                           true, false);
+
+      elfcpp::Ehdr<size, big_endian> ehdr(pehdr);
+      elfcpp::Elf_Word in_flags = ehdr.get_e_flags();
+      unsigned char ei_class = ehdr.get_e_ident()[elfcpp::EI_CLASS];
+
+      this->merge_processor_specific_flags(dynobj->name(), in_flags, ei_class,
+                                           true);
+    }
+
+  // Merge .reginfo contents of input objects.
+  Valtype gprmask = 0;
+  Valtype cprmask1 = 0;
+  Valtype cprmask2 = 0;
+  Valtype cprmask3 = 0;
+  Valtype cprmask4 = 0;
+  for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
+       p != input_objects->relobj_end();
+       ++p)
+    {
+      Mips_relobj<size, big_endian>* relobj =
+        Mips_relobj<size, big_endian>::as_mips_relobj(*p);
+
+      gprmask |= relobj->gprmask();
+      cprmask1 |= relobj->cprmask1();
+      cprmask2 |= relobj->cprmask2();
+      cprmask3 |= relobj->cprmask3();
+      cprmask4 |= relobj->cprmask4();
+    }
+
+  if (this->plt_ != NULL)
+    {
+      // Set final PLT offsets for symbols.
+      this->plt_section()->set_plt_offsets();
+
+      // Define _PROCEDURE_LINKAGE_TABLE_ at the start of the .plt section.
+      // Set STO_MICROMIPS flag if the output has microMIPS code, but only if
+      // there are no standard PLT entries present.
+      unsigned char nonvis = 0;
+      if (this->is_output_micromips()
+          && !this->plt_section()->has_standard_entries())
+        nonvis = elfcpp::STO_MICROMIPS >> 2;
+      symtab->define_in_output_data("_PROCEDURE_LINKAGE_TABLE_", NULL,
+                                    Symbol_table::PREDEFINED,
+                                    this->plt_,
+                                    0, 0, elfcpp::STT_FUNC,
+                                    elfcpp::STB_LOCAL,
+                                    elfcpp::STV_DEFAULT, nonvis,
+                                    false, false);
+    }
+
+  if (this->mips_stubs_ != NULL)
+    {
+      // Define _MIPS_STUBS_ at the start of the .MIPS.stubs section.
+      unsigned char nonvis = 0;
+      if (this->is_output_micromips())
+        nonvis = elfcpp::STO_MICROMIPS >> 2;
+      symtab->define_in_output_data("_MIPS_STUBS_", NULL,
+                                    Symbol_table::PREDEFINED,
+                                    this->mips_stubs_,
+                                    0, 0, elfcpp::STT_FUNC,
+                                    elfcpp::STB_LOCAL,
+                                    elfcpp::STV_DEFAULT, nonvis,
+                                    false, false);
+    }
+
+  if (!parameters->options().relocatable() && !parameters->doing_static_link())
+    // In case there is no .got section, create one.
+    this->got_section(symtab, layout);
+
+  // Emit any relocs we saved in an attempt to avoid generating COPY
+  // relocs.
+  if (this->copy_relocs_.any_saved_relocs())
+    this->copy_relocs_.emit_mips(this->rel_dyn_section(layout), symtab, layout,
+                                 this);
+
+  // Emit dynamic relocs.
+  for (typename std::vector<Dyn_reloc>::iterator p = this->dyn_relocs_.begin();
+       p != this->dyn_relocs_.end();
+       ++p)
+    p->emit(this->rel_dyn_section(layout), this->got_section(), symtab);
+
+  if (this->has_got_section())
+    this->got_section()->lay_out_got(layout, symtab, input_objects);
+
+  if (this->mips_stubs_ != NULL)
+    this->mips_stubs_->set_needs_dynsym_value();
+
+  // Check for functions that might need $25 to be valid on entry.
+  // TODO(sasa): Can we do this without iterating over all symbols?
+  typedef Symbol_visitor_check_symbols<size, big_endian> Symbol_visitor;
+  symtab->for_all_symbols<size, Symbol_visitor>(Symbol_visitor(this, layout,
+                                                               symtab));
+
+  // Add NULL segment.
+  if (!parameters->options().relocatable())
+    layout->make_output_segment(elfcpp::PT_NULL, 0);
+
+  for (Layout::Section_list::const_iterator p = layout->section_list().begin();
+       p != layout->section_list().end();
+       ++p)
+    {
+      if ((*p)->type() == elfcpp::SHT_MIPS_REGINFO)
+        {
+          Mips_output_section_reginfo<size, big_endian>* reginfo =
+            Mips_output_section_reginfo<size, big_endian>::
+              as_mips_output_section_reginfo(*p);
+
+          reginfo->set_masks(gprmask, cprmask1, cprmask2, cprmask3, cprmask4);
+
+          if (!parameters->options().relocatable())
+            {
+              Output_segment* reginfo_segment =
+                layout->make_output_segment(elfcpp::PT_MIPS_REGINFO,
+                                            elfcpp::PF_R);
+              reginfo_segment->add_output_section_to_nonload(reginfo,
+                                                             elfcpp::PF_R);
+            }
+        }
+    }
+
+  // Fill in some more dynamic tags.
+  // TODO(sasa): Add more dynamic tags.
+  const Reloc_section* rel_plt = (this->plt_ == NULL
+                                  ? NULL : this->plt_->rel_plt());
+  layout->add_target_dynamic_tags(true, this->got_, rel_plt,
+                                  this->rel_dyn_, true, false);
+
+  Output_data_dynamic* const odyn = layout->dynamic_data();
+  if (odyn != NULL
+      && !parameters->options().relocatable()
+      && !parameters->doing_static_link())
+  {
+    unsigned int d_val;
+    // This element holds a 32-bit version id for the Runtime
+    // Linker Interface.  This will start at integer value 1.
+    d_val = 0x01;
+    odyn->add_constant(elfcpp::DT_MIPS_RLD_VERSION, d_val);
+
+    // Dynamic flags
+    d_val = elfcpp::RHF_NOTPOT;
+    odyn->add_constant(elfcpp::DT_MIPS_FLAGS, d_val);
+
+    // Save layout for using when emiting custom dynamic tags.
+    this->layout_ = layout;
+
+    // This member holds the base address of the segment.
+    odyn->add_custom(elfcpp::DT_MIPS_BASE_ADDRESS);
+
+    // This member holds the number of entries in the .dynsym section.
+    odyn->add_custom(elfcpp::DT_MIPS_SYMTABNO);
+
+    // This member holds the index of the first dynamic symbol
+    // table entry that corresponds to an entry in the global offset table.
+    odyn->add_custom(elfcpp::DT_MIPS_GOTSYM);
+
+    // This member holds the number of local GOT entries.
+    odyn->add_constant(elfcpp::DT_MIPS_LOCAL_GOTNO,
+                       this->got_->get_local_gotno());
+
+    if (this->plt_ != NULL)
+      // DT_MIPS_PLTGOT dynamic tag
+      odyn->add_section_address(elfcpp::DT_MIPS_PLTGOT, this->got_plt_);
+  }
+ }
+
+// Get the custom dynamic tag value.
+template<int size, bool big_endian>
+unsigned int
+Target_mips<size, big_endian>::do_dynamic_tag_custom_value(elfcpp::DT tag) const
+{
+  switch (tag)
+    {
+    case elfcpp::DT_MIPS_BASE_ADDRESS:
+      {
+        // The base address of the segment.
+        // At this point, the segment list has been sorted into final order,
+        // so just return vaddr of the first readable PT_LOAD segment.
+        Output_segment* seg =
+          this->layout_->find_output_segment(elfcpp::PT_LOAD, elfcpp::PF_R, 0);
+        gold_assert(seg != NULL);
+        return seg->vaddr();
+      }
+
+    case elfcpp::DT_MIPS_SYMTABNO:
+      // The number of entries in the .dynsym section.
+      return this->get_dt_mips_symtabno();
+
+    case elfcpp::DT_MIPS_GOTSYM:
+      {
+        // The index of the first dynamic symbol table entry that corresponds
+        // to an entry in the GOT.
+        if (this->got_->first_global_got_dynsym_index() != -1U)
+          return this->got_->first_global_got_dynsym_index();
+        else
+          // In case if we don't have global GOT symbols we default to setting
+          // DT_MIPS_GOTSYM to the same value as DT_MIPS_SYMTABNO.
+          return this->get_dt_mips_symtabno();
+      }
+
+    default:
+      gold_error(_("Unknown dynamic tag 0x%x"), (unsigned int)tag);
+    }
+
+  return (unsigned int)-1;
+}
+
+// Relocate section data.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::relocate_section(
+                        const Relocate_info<size, big_endian>* relinfo,
+                        unsigned int sh_type,
+                        const unsigned char* prelocs,
+                        size_t reloc_count,
+                        Output_section* output_section,
+                        bool needs_special_offset_handling,
+                        unsigned char* view,
+                        Mips_address address,
+                        section_size_type view_size,
+                        const Reloc_symbol_changes* reloc_symbol_changes)
+{
+  typedef Target_mips<size, big_endian> Mips;
+  typedef typename Target_mips<size, big_endian>::Relocate Mips_relocate;
+
+  if (sh_type == elfcpp::SHT_REL)
+    gold::relocate_section<size, big_endian, Mips, elfcpp::SHT_REL,
+      Mips_relocate, gold::Default_comdat_behavior>(
+      relinfo,
+      this,
+      prelocs,
+      reloc_count,
+      output_section,
+      needs_special_offset_handling,
+      view,
+      address,
+      view_size,
+      reloc_symbol_changes);
+  else if (sh_type == elfcpp::SHT_RELA)
+    gold::relocate_section<size, big_endian, Mips, elfcpp::SHT_RELA,
+      Mips_relocate, gold::Default_comdat_behavior>(
+      relinfo,
+      this,
+      prelocs,
+      reloc_count,
+      output_section,
+      needs_special_offset_handling,
+      view,
+      address,
+      view_size,
+     reloc_symbol_changes);
+}
+
+// Return the size of a relocation while scanning during a relocatable
+// link.
+
+template<int size, bool big_endian>
+unsigned int
+Target_mips<size, big_endian>::Relocatable_size_for_reloc::get_size_for_reloc(
+    unsigned int r_type,
+    Relobj* object)
+{
+  switch (r_type)
+    {
+    case elfcpp::R_MIPS_NONE:
+    case elfcpp::R_MIPS_TLS_DTPMOD64:
+    case elfcpp::R_MIPS_TLS_DTPREL64:
+    case elfcpp::R_MIPS_TLS_TPREL64:
+      return 0;
+
+    case elfcpp::R_MIPS_32:
+    case elfcpp::R_MIPS_TLS_DTPMOD32:
+    case elfcpp::R_MIPS_TLS_DTPREL32:
+    case elfcpp::R_MIPS_TLS_TPREL32:
+    case elfcpp::R_MIPS_REL32:
+    case elfcpp::R_MIPS_PC32:
+    case elfcpp::R_MIPS_GPREL32:
+    case elfcpp::R_MIPS_JALR:
+      return 4;
+
+    case elfcpp::R_MIPS_16:
+    case elfcpp::R_MIPS_HI16:
+    case elfcpp::R_MIPS_LO16:
+    case elfcpp::R_MIPS_GPREL16:
+    case elfcpp::R_MIPS16_HI16:
+    case elfcpp::R_MIPS16_LO16:
+    case elfcpp::R_MIPS_PC16:
+    case elfcpp::R_MIPS_GOT16:
+    case elfcpp::R_MIPS16_GOT16:
+    case elfcpp::R_MIPS_CALL16:
+    case elfcpp::R_MIPS16_CALL16:
+    case elfcpp::R_MIPS_GOT_HI16:
+    case elfcpp::R_MIPS_CALL_HI16:
+    case elfcpp::R_MIPS_GOT_LO16:
+    case elfcpp::R_MIPS_CALL_LO16:
+    case elfcpp::R_MIPS_TLS_DTPREL_HI16:
+    case elfcpp::R_MIPS_TLS_DTPREL_LO16:
+    case elfcpp::R_MIPS_TLS_TPREL_HI16:
+    case elfcpp::R_MIPS_TLS_TPREL_LO16:
+    case elfcpp::R_MIPS16_GPREL:
+    case elfcpp::R_MIPS_GOT_DISP:
+    case elfcpp::R_MIPS_LITERAL:
+    case elfcpp::R_MIPS_GOT_PAGE:
+    case elfcpp::R_MIPS_GOT_OFST:
+    case elfcpp::R_MIPS_TLS_GD:
+    case elfcpp::R_MIPS_TLS_LDM:
+    case elfcpp::R_MIPS_TLS_GOTTPREL:
+      return 2;
+
+    // These relocations are not byte sized
+    case elfcpp::R_MIPS_26:
+    case elfcpp::R_MIPS16_26:
+      return 4;
+
+    case elfcpp::R_MIPS_COPY:
+    case elfcpp::R_MIPS_JUMP_SLOT:
+      object->error(_("unexpected reloc %u in object file"), r_type);
+      return 0;
+
+    default:
+      object->error(_("unsupported reloc %u in object file"), r_type);
+      return 0;
+  }
+}
+
+// Scan the relocs during a relocatable link.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::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* rr)
+{
+  gold_assert(sh_type == elfcpp::SHT_REL);
+
+  typedef Mips_scan_relocatable_relocs<big_endian, elfcpp::SHT_REL,
+    Relocatable_size_for_reloc> Scan_relocatable_relocs;
+
+  gold::scan_relocatable_relocs<size, big_endian, elfcpp::SHT_REL,
+    Scan_relocatable_relocs>(
+    symtab,
+    layout,
+    object,
+    data_shndx,
+    prelocs,
+    reloc_count,
+    output_section,
+    needs_special_offset_handling,
+    local_symbol_count,
+    plocal_symbols,
+    rr);
+}
+
+// Emit relocations for a section.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::relocate_relocs(
+                        const Relocate_info<size, big_endian>* relinfo,
+                        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* rr,
+                        unsigned char* view,
+                        Mips_address view_address,
+                        section_size_type view_size,
+                        unsigned char* reloc_view,
+                        section_size_type reloc_view_size)
+{
+  gold_assert(sh_type == elfcpp::SHT_REL);
+
+  gold::relocate_relocs<size, big_endian, elfcpp::SHT_REL>(
+    relinfo,
+    prelocs,
+    reloc_count,
+    output_section,
+    offset_in_output_section,
+    rr,
+    view,
+    view_address,
+    view_size,
+    reloc_view,
+    reloc_view_size);
+}
+
+// Perform target-specific processing in a relocatable link.  This is
+// only used if we use the relocation strategy RELOC_SPECIAL.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::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,
+    Mips_address view_address,
+    section_size_type,
+    unsigned char* preloc_out)
+{
+  // We can only handle REL type relocation sections.
+  gold_assert(sh_type == elfcpp::SHT_REL);
+
+  typedef typename Reloc_types<elfcpp::SHT_REL, size, big_endian>::Reloc
+    Reltype;
+  typedef typename Reloc_types<elfcpp::SHT_REL, size, big_endian>::Reloc_write
+    Reltype_write;
+
+  typedef Mips_relocate_functions<size, big_endian> Reloc_funcs;
+
+  const Mips_address invalid_address = static_cast<Mips_address>(0) - 1;
+
+  Mips_relobj<size, big_endian>* object =
+    Mips_relobj<size, big_endian>::as_mips_relobj(relinfo->object);
+  const unsigned int local_count = object->local_symbol_count();
+
+  Reltype reloc(preloc_in);
+  Reltype_write reloc_write(preloc_out);
+
+  elfcpp::Elf_types<32>::Elf_WXword r_info = reloc.get_r_info();
+  const unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+  const unsigned int r_type = elfcpp::elf_r_type<size>(r_info);
+
+  // Get the new symbol index.
+  // We only use RELOC_SPECIAL strategy in local relocations.
+  gold_assert(r_sym < local_count);
+
+  // We are adjusting a section symbol.  We need to find
+  // the symbol table index of the section symbol for
+  // the output section corresponding to input section
+  // in which this symbol is defined.
+  bool is_ordinary;
+  unsigned int shndx = object->local_symbol_input_shndx(r_sym, &is_ordinary);
+  gold_assert(is_ordinary);
+  Output_section* os = object->output_section(shndx);
+  gold_assert(os != NULL);
+  gold_assert(os->needs_symtab_index());
+  unsigned int new_symndx = os->symtab_index();
+
+  // Get the new offset--the location in the output section where
+  // this relocation should be applied.
+
+  Mips_address offset = reloc.get_r_offset();
+  Mips_address new_offset;
+  if (offset_in_output_section != invalid_address)
+    new_offset = offset + offset_in_output_section;
+  else
+    {
+      section_offset_type sot_offset =
+        convert_types<section_offset_type, Mips_address>(offset);
+      section_offset_type new_sot_offset =
+        output_section->output_offset(object, relinfo->data_shndx,
+                                      sot_offset);
+      gold_assert(new_sot_offset != -1);
+      new_offset = new_sot_offset;
+    }
+
+  // In an object file, r_offset is an offset within the section.
+  // In an executable or dynamic object, generated by
+  // --emit-relocs, r_offset is an absolute address.
+  if (!parameters->options().relocatable())
+    {
+      new_offset += view_address;
+      if (offset_in_output_section != invalid_address)
+        new_offset -= offset_in_output_section;
+    }
+
+  reloc_write.put_r_offset(new_offset);
+  reloc_write.put_r_info(elfcpp::elf_r_info<32>(new_symndx, r_type));
+
+  // Handle the reloc addend.
+  // The relocation uses a section symbol in the input file.
+  // We are adjusting it to use a section symbol in the output
+  // file.  The input section symbol refers to some address in
+  // the input section.  We need the relocation in the output
+  // file to refer to that same address.  This adjustment to
+  // the addend is the same calculation we use for a simple
+  // absolute relocation for the input section symbol.
+
+  const Symbol_value<size>* psymval = object->local_symbol(r_sym);
+
+  unsigned char* paddend = view + offset;
+  typename Reloc_funcs::Status reloc_status = Reloc_funcs::STATUS_OKAY;
+  switch (r_type)
+    {
+    case elfcpp::R_MIPS_26:
+      reloc_status = Reloc_funcs::rel26(paddend, object, psymval,
+          offset_in_output_section, true, 0, sh_type == elfcpp::SHT_REL, NULL,
+          false /*TODO(sasa): cross mode jump*/, r_type, this->jal_to_bal());
+      break;
+
+    default:
+      gold_unreachable();
+    }
+
+  // Report any errors.
+  switch (reloc_status)
+    {
+    case Reloc_funcs::STATUS_OKAY:
+      break;
+    case Reloc_funcs::STATUS_OVERFLOW:
+      gold_error_at_location(relinfo, relnum, reloc.get_r_offset(),
+                             _("relocation overflow"));
+      break;
+    case Reloc_funcs::STATUS_BAD_RELOC:
+      gold_error_at_location(relinfo, relnum, reloc.get_r_offset(),
+        _("unexpected opcode while processing relocation"));
+      break;
+    default:
+      gold_unreachable();
+    }
+}
+
+// Optimize the TLS relocation type based on what we know about the
+// symbol.  IS_FINAL is true if the final address of this symbol is
+// known at link time.
+
+template<int size, bool big_endian>
+tls::Tls_optimization
+Target_mips<size, big_endian>::optimize_tls_reloc(bool, int)
+{
+  // FIXME: Currently we do not do any TLS optimization.
+  return tls::TLSOPT_NONE;
+}
+
+// Scan a relocation for a local symbol.
+
+template<int size, bool big_endian>
+inline void
+Target_mips<size, big_endian>::Scan::local(
+                        Symbol_table* symtab,
+                        Layout* layout,
+                        Target_mips<size, big_endian>* 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)
+{
+  if (is_discarded)
+    return;
+
+  Mips_address r_offset;
+  typename elfcpp::Elf_types<size>::Elf_WXword r_info;
+  typename elfcpp::Elf_types<size>::Elf_Swxword r_addend;
+
+  if (rel_type == elfcpp::SHT_RELA)
+    {
+      r_offset = rela->get_r_offset();
+      r_info = rela->get_r_info();
+      r_addend = rela->get_r_addend();
+    }
+  else
+    {
+      r_offset = rel->get_r_offset();
+      r_info = rel->get_r_info();
+      r_addend = 0;
+    }
+
+  unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+  Mips_relobj<size, big_endian>* mips_obj =
+    Mips_relobj<size, big_endian>::as_mips_relobj(object);
+
+  if (mips_obj->is_mips16_stub_section(data_shndx))
+    {
+      mips_obj->get_mips16_stub_section(data_shndx)
+              ->new_local_reloc_found(r_type, r_sym);
+    }
+
+  if (r_type == elfcpp::R_MIPS_NONE)
+    // R_MIPS_NONE is used in mips16 stub sections, to define the target of the
+    // mips16 stub.
+    return;
+
+  if (!mips16_call_reloc(r_type)
+      && !mips_obj->section_allows_mips16_refs(data_shndx))
+    // This reloc would need to refer to a MIPS16 hard-float stub, if
+    // there is one.  We ignore MIPS16 stub sections and .pdr section when
+    // looking for relocs that would need to refer to MIPS16 stubs.
+    mips_obj->add_local_non_16bit_call(r_sym);
+
+  if (r_type == elfcpp::R_MIPS16_26
+      && !mips_obj->section_allows_mips16_refs(data_shndx))
+    mips_obj->add_local_16bit_call(r_sym);
+
+  switch (r_type)
+    {
+    case elfcpp::R_MIPS_GOT16:
+    case elfcpp::R_MIPS_CALL16:
+    case elfcpp::R_MIPS_CALL_HI16:
+    case elfcpp::R_MIPS_CALL_LO16:
+    case elfcpp::R_MIPS_GOT_HI16:
+    case elfcpp::R_MIPS_GOT_LO16:
+    case elfcpp::R_MIPS_GOT_PAGE:
+    case elfcpp::R_MIPS_GOT_OFST:
+    case elfcpp::R_MIPS_GOT_DISP:
+    case elfcpp::R_MIPS_TLS_GOTTPREL:
+    case elfcpp::R_MIPS_TLS_GD:
+    case elfcpp::R_MIPS_TLS_LDM:
+    case elfcpp::R_MIPS16_GOT16:
+    case elfcpp::R_MIPS16_CALL16:
+    case elfcpp::R_MIPS16_TLS_GOTTPREL:
+    case elfcpp::R_MIPS16_TLS_GD:
+    case elfcpp::R_MIPS16_TLS_LDM:
+    case elfcpp::R_MICROMIPS_GOT16:
+    case elfcpp::R_MICROMIPS_CALL16:
+    case elfcpp::R_MICROMIPS_CALL_HI16:
+    case elfcpp::R_MICROMIPS_CALL_LO16:
+    case elfcpp::R_MICROMIPS_GOT_HI16:
+    case elfcpp::R_MICROMIPS_GOT_LO16:
+    case elfcpp::R_MICROMIPS_GOT_PAGE:
+    case elfcpp::R_MICROMIPS_GOT_OFST:
+    case elfcpp::R_MICROMIPS_GOT_DISP:
+    case elfcpp::R_MICROMIPS_TLS_GOTTPREL:
+    case elfcpp::R_MICROMIPS_TLS_GD:
+    case elfcpp::R_MICROMIPS_TLS_LDM:
+      // We need a GOT section.
+      target->got_section(symtab, layout);
+      break;
+
+    default:
+      break;
+    }
+
+  if (call_lo16_reloc(r_type)
+      || got_lo16_reloc(r_type)
+      || got_disp_reloc(r_type))
+    {
+      // We may need a local GOT entry for this relocation.  We
+      // don't count R_MIPS_GOT_PAGE because we can estimate the
+      // maximum number of pages needed by looking at the size of
+      // the segment.  Similar comments apply to R_MIPS*_GOT16 and
+      // R_MIPS*_CALL16.  We don't count R_MIPS_GOT_HI16, or
+      // R_MIPS_CALL_HI16 because these are always followed by an
+      // R_MIPS_GOT_LO16 or R_MIPS_CALL_LO16.
+      Mips_output_data_got<size, big_endian>* got =
+        target->got_section(symtab, layout);
+      unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+      got->record_local_got_symbol(mips_obj, r_sym, r_addend, r_type, -1U);
+    }
+
+  switch (r_type)
+    {
+    case elfcpp::R_MIPS_CALL16:
+    case elfcpp::R_MIPS16_CALL16:
+    case elfcpp::R_MICROMIPS_CALL16:
+      gold_error(_("CALL16 reloc at 0x%lx not against global symbol "),
+                 (unsigned long)r_offset);
+      return;
+
+    case elfcpp::R_MIPS_GOT_PAGE:
+    case elfcpp::R_MICROMIPS_GOT_PAGE:
+    case elfcpp::R_MIPS16_GOT16:
+    case elfcpp::R_MIPS_GOT16:
+    case elfcpp::R_MIPS_GOT_HI16:
+    case elfcpp::R_MIPS_GOT_LO16:
+    case elfcpp::R_MICROMIPS_GOT16:
+    case elfcpp::R_MICROMIPS_GOT_HI16:
+    case elfcpp::R_MICROMIPS_GOT_LO16:
+      {
+        // This relocation needs a page entry in the GOT.
+        // Get the section contents.
+        section_size_type view_size = 0;
+        const unsigned char* view = object->section_contents(data_shndx,
+                                                             &view_size, false);
+        view += r_offset;
+
+        Valtype32 val = elfcpp::Swap<32, big_endian>::readval(view);
+        Valtype32 addend = (rel_type == elfcpp::SHT_REL ? val & 0xffff
+                                                        : r_addend);
+
+        if (rel_type == elfcpp::SHT_REL && got16_reloc(r_type))
+          target->got16_addends_.push_back(got16_addend<size, big_endian>(
+              object, data_shndx, r_type, r_sym, addend));
+        else
+          target->got_section()->record_got_page_entry(mips_obj, r_sym, addend);
+        break;
+      }
+
+    case elfcpp::R_MIPS_HI16:
+    case elfcpp::R_MIPS16_HI16:
+    case elfcpp::R_MICROMIPS_HI16:
+      // Record the reloc so that we can check whether the corresponding LO16
+      // part exists.
+      if (rel_type == elfcpp::SHT_REL)
+        target->got16_addends_.push_back(got16_addend<size, big_endian>(
+            object, data_shndx, r_type, r_sym, 0));
+      break;
+
+    case elfcpp::R_MIPS_LO16:
+    case elfcpp::R_MIPS16_LO16:
+    case elfcpp::R_MICROMIPS_LO16:
+      {
+        if (rel_type != elfcpp::SHT_REL)
+          break;
+
+        // Find corresponding GOT16/HI16 relocation.
+
+        // According to the MIPS ELF ABI, the R_MIPS_LO16 relocation must
+        // be immediately following.  However, for the IRIX6 ABI, the next
+        // relocation may be a composed relocation consisting of several
+        // relocations for the same address.  In that case, the R_MIPS_LO16
+        // relocation may occur as one of these.  We permit a similar
+        // extension in general, as that is useful for GCC.
+
+        // In some cases GCC dead code elimination removes the LO16 but
+        // keeps the corresponding HI16.  This is strictly speaking a
+        // violation of the ABI but not immediately harmful.
+
+        typename std::list<got16_addend<size, big_endian> >::iterator it =
+          target->got16_addends_.begin();
+        while (it != target->got16_addends_.end())
+          {
+            got16_addend<size, big_endian> _got16_addend = *it;
+
+            // TODO(sasa): Split got16_addends_ list into two lists - one for
+            // GOT16 relocs and the other for HI16 relocs.
+
+            // Report an error if we find HI16 or GOT16 reloc from the
+            // previous section without the matching LO16 part.
+            if (_got16_addend.object != object
+                || _got16_addend.shndx != data_shndx)
+              {
+                gold_error("Can't find matching LO16 reloc");
+                break;
+              }
+
+            if (_got16_addend.r_sym != r_sym
+                || !is_matching_lo16_reloc(_got16_addend.r_type, r_type))
+              {
+                ++it;
+                continue;
+              }
+
+            // We found a matching HI16 or GOT16 reloc for this LO16 reloc.
+            // For GOT16, we need to calculate combined addend and record GOT page
+            // entry.
+            if (got16_reloc(_got16_addend.r_type))
+              {
+
+                section_size_type view_size = 0;
+                const unsigned char* view = object->section_contents(data_shndx,
+                                                                     &view_size,
+                                                                     false);
+                view += r_offset;
+
+                Valtype32 val = elfcpp::Swap<32, big_endian>::readval(view);
+                int32_t addend = Bits<16>::sign_extend32(val & 0xffff);
+
+                addend = (_got16_addend.addend << 16) + addend;
+                target->got_section()->record_got_page_entry(mips_obj, r_sym,
+                                                             addend);
+              }
+
+            it = target->got16_addends_.erase(it);
+          }
+        break;
+      }
+    }
+
+  switch (r_type)
+    {
+    case elfcpp::R_MIPS_32:
+    case elfcpp::R_MIPS_REL32:
+    case elfcpp::R_MIPS_64:
+      {
+        if (parameters->options().output_is_position_independent())
+          {
+            // If building a shared library (or a position-independent
+            // executable), we need to create a dynamic relocation for
+            // this location.
+            Reloc_section* rel_dyn = target->rel_dyn_section(layout);
+            unsigned int r_sym = elfcpp::elf_r_sym<32>(r_info);
+            rel_dyn->add_symbolless_local_addend(object, r_sym,
+                                                 elfcpp::R_MIPS_REL32,
+                                                 output_section, data_shndx,
+                                                 r_offset);
+          }
+        break;
+      }
+
+    case elfcpp::R_MIPS_TLS_GOTTPREL:
+    case elfcpp::R_MIPS16_TLS_GOTTPREL:
+    case elfcpp::R_MICROMIPS_TLS_GOTTPREL:
+    case elfcpp::R_MIPS_TLS_LDM:
+    case elfcpp::R_MIPS16_TLS_LDM:
+    case elfcpp::R_MICROMIPS_TLS_LDM:
+    case elfcpp::R_MIPS_TLS_GD:
+    case elfcpp::R_MIPS16_TLS_GD:
+    case elfcpp::R_MICROMIPS_TLS_GD:
+      {
+        unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+        bool output_is_shared = parameters->options().shared();
+        const tls::Tls_optimization optimized_type
+            = Target_mips<size, big_endian>::optimize_tls_reloc(
+                                             !output_is_shared, r_type);
+        switch (r_type)
+          {
+          case elfcpp::R_MIPS_TLS_GD:
+          case elfcpp::R_MIPS16_TLS_GD:
+          case elfcpp::R_MICROMIPS_TLS_GD:
+            if (optimized_type == tls::TLSOPT_NONE)
+              {
+                // Create a pair of GOT entries for the module index and
+                // dtv-relative offset.
+                Mips_output_data_got<size, big_endian>* got =
+                  target->got_section(symtab, layout);
+                unsigned int shndx = lsym.get_st_shndx();
+                bool is_ordinary;
+                shndx = object->adjust_sym_shndx(r_sym, shndx, &is_ordinary);
+                if (!is_ordinary)
+                  {
+                    object->error(_("local symbol %u has bad shndx %u"),
+                                  r_sym, shndx);
+                    break;
+                  }
+                got->record_local_got_symbol(mips_obj, r_sym, r_addend, r_type,
+                                             shndx);
+              }
+            else
+              {
+                // FIXME: TLS optimization not supported yet.
+                gold_unreachable();
+              }
+            break;
+
+          case elfcpp::R_MIPS_TLS_LDM:
+          case elfcpp::R_MIPS16_TLS_LDM:
+          case elfcpp::R_MICROMIPS_TLS_LDM:
+            if (optimized_type == tls::TLSOPT_NONE)
+              {
+                // We always record LDM symbols as local with index 0.
+                target->got_section()->record_local_got_symbol(mips_obj, 0,
+                                                               r_addend, r_type,
+                                                               -1U);
+              }
+            else
+              {
+                // FIXME: TLS optimization not supported yet.
+                gold_unreachable();
+              }
+            break;
+          case elfcpp::R_MIPS_TLS_GOTTPREL:
+          case elfcpp::R_MIPS16_TLS_GOTTPREL:
+          case elfcpp::R_MICROMIPS_TLS_GOTTPREL:
+            layout->set_has_static_tls();
+            if (optimized_type == tls::TLSOPT_NONE)
+              {
+                // Create a GOT entry for the tp-relative offset.
+                Mips_output_data_got<size, big_endian>* got =
+                  target->got_section(symtab, layout);
+                got->record_local_got_symbol(mips_obj, r_sym, r_addend, r_type,
+                                             -1U);
+              }
+            else
+              {
+                // FIXME: TLS optimization not supported yet.
+                gold_unreachable();
+              }
+            break;
+
+          default:
+            gold_unreachable();
+        }
+      }
+      break;
+
+    default:
+      break;
+    }
+
+  // Refuse some position-dependent relocations when creating a
+  // shared library.  Do not refuse R_MIPS_32 / R_MIPS_64; they're
+  // not PIC, but we can create dynamic relocations and the result
+  // will be fine.  Also do not refuse R_MIPS_LO16, which can be
+  // combined with R_MIPS_GOT16.
+  if (parameters->options().shared())
+    {
+      switch (r_type)
+        {
+        case elfcpp::R_MIPS16_HI16:
+        case elfcpp::R_MIPS_HI16:
+        case elfcpp::R_MICROMIPS_HI16:
+          // Don't refuse a high part relocation if it's against
+          // no symbol (e.g. part of a compound relocation).
+          if (r_sym == 0)
+            break;
+
+          // FALLTHROUGH
+
+        case elfcpp::R_MIPS16_26:
+        case elfcpp::R_MIPS_26:
+        case elfcpp::R_MICROMIPS_26_S1:
+          gold_error(_("%s: relocation %u against `%s' can not be used when "
+                       "making a shared object; recompile with -fPIC"),
+                     object->name().c_str(), r_type, "a local symbol");
+        default:
+          break;
+        }
+    }
+}
+
+template<int size, bool big_endian>
+inline void
+Target_mips<size, big_endian>::Scan::local(
+                        Symbol_table* symtab,
+                        Layout* layout,
+                        Target_mips<size, big_endian>* 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)
+{
+  if (is_discarded)
+    return;
+
+  local(
+    symtab,
+    layout,
+    target,
+    object,
+    data_shndx,
+    output_section,
+    (const elfcpp::Rela<size, big_endian>*) NULL,
+    &reloc,
+    elfcpp::SHT_REL,
+    r_type,
+    lsym, is_discarded);
+}
+
+
+template<int size, bool big_endian>
+inline void
+Target_mips<size, big_endian>::Scan::local(
+                        Symbol_table* symtab,
+                        Layout* layout,
+                        Target_mips<size, big_endian>* 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)
+{
+  if (is_discarded)
+    return;
+
+  local(
+    symtab,
+    layout,
+    target,
+    object,
+    data_shndx,
+    output_section,
+    &reloc,
+    (const elfcpp::Rel<size, big_endian>*) NULL,
+    elfcpp::SHT_RELA,
+    r_type,
+    lsym, is_discarded);
+}
+
+// Scan a relocation for a global symbol.
+
+template<int size, bool big_endian>
+inline void
+Target_mips<size, big_endian>::Scan::global(
+                                Symbol_table* symtab,
+                                Layout* layout,
+                                Target_mips<size, big_endian>* 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)
+{
+  Mips_address r_offset;
+  typename elfcpp::Elf_types<size>::Elf_WXword r_info;
+  typename elfcpp::Elf_types<size>::Elf_Swxword r_addend;
+
+  if (rel_type == elfcpp::SHT_RELA)
+    {
+      r_offset = rela->get_r_offset();
+      r_info = rela->get_r_info();
+      r_addend = rela->get_r_addend();
+    }
+  else
+    {
+      r_offset = rel->get_r_offset();
+      r_info = rel->get_r_info();
+      r_addend = 0;
+    }
+
+  unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+  Mips_relobj<size, big_endian>* mips_obj =
+    Mips_relobj<size, big_endian>::as_mips_relobj(object);
+  Mips_symbol<size>* mips_sym = Mips_symbol<size>::as_mips_sym(gsym);
+
+  if (mips_obj->is_mips16_stub_section(data_shndx))
+    {
+      mips_obj->get_mips16_stub_section(data_shndx)
+              ->new_global_reloc_found(r_type, mips_sym);
+    }
+
+  if (r_type == elfcpp::R_MIPS_NONE)
+    // R_MIPS_NONE is used in mips16 stub sections, to define the target of the
+    // mips16 stub.
+    return;
+
+  if (!mips16_call_reloc(r_type)
+      && !mips_obj->section_allows_mips16_refs(data_shndx))
+    // This reloc would need to refer to a MIPS16 hard-float stub, if
+    // there is one.  We ignore MIPS16 stub sections and .pdr section when
+    // looking for relocs that would need to refer to MIPS16 stubs.
+    mips_sym->set_need_fn_stub();
+
+  // A reference to _GLOBAL_OFFSET_TABLE_ implies that we need a got
+  // section.  We check here to avoid creating a dynamic reloc against
+  // _GLOBAL_OFFSET_TABLE_.
+  if (!target->has_got_section()
+      && strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
+    target->got_section(symtab, layout);
+
+  // We need PLT entries if there are static-only relocations against
+  // an externally-defined function.  This can technically occur for
+  // shared libraries if there are branches to the symbol, although it
+  // is unlikely that this will be used in practice due to the short
+  // ranges involved.  It can occur for any relative or absolute relocation
+  // in executables; in that case, the PLT entry becomes the function's
+  // canonical address.
+  bool static_reloc = false;
+
+  // Set CAN_MAKE_DYNAMIC to true if we can convert this
+  // relocation into a dynamic one.
+  bool can_make_dynamic = false;
+  switch (r_type)
+    {
+    case elfcpp::R_MIPS_GOT16:
+    case elfcpp::R_MIPS_CALL16:
+    case elfcpp::R_MIPS_CALL_HI16:
+    case elfcpp::R_MIPS_CALL_LO16:
+    case elfcpp::R_MIPS_GOT_HI16:
+    case elfcpp::R_MIPS_GOT_LO16:
+    case elfcpp::R_MIPS_GOT_PAGE:
+    case elfcpp::R_MIPS_GOT_OFST:
+    case elfcpp::R_MIPS_GOT_DISP:
+    case elfcpp::R_MIPS_TLS_GOTTPREL:
+    case elfcpp::R_MIPS_TLS_GD:
+    case elfcpp::R_MIPS_TLS_LDM:
+    case elfcpp::R_MIPS16_GOT16:
+    case elfcpp::R_MIPS16_CALL16:
+    case elfcpp::R_MIPS16_TLS_GOTTPREL:
+    case elfcpp::R_MIPS16_TLS_GD:
+    case elfcpp::R_MIPS16_TLS_LDM:
+    case elfcpp::R_MICROMIPS_GOT16:
+    case elfcpp::R_MICROMIPS_CALL16:
+    case elfcpp::R_MICROMIPS_CALL_HI16:
+    case elfcpp::R_MICROMIPS_CALL_LO16:
+    case elfcpp::R_MICROMIPS_GOT_HI16:
+    case elfcpp::R_MICROMIPS_GOT_LO16:
+    case elfcpp::R_MICROMIPS_GOT_PAGE:
+    case elfcpp::R_MICROMIPS_GOT_OFST:
+    case elfcpp::R_MICROMIPS_GOT_DISP:
+    case elfcpp::R_MICROMIPS_TLS_GOTTPREL:
+    case elfcpp::R_MICROMIPS_TLS_GD:
+    case elfcpp::R_MICROMIPS_TLS_LDM:
+      // We need a GOT section.
+      target->got_section(symtab, layout);
+      break;
+
+    // This is just a hint; it can safely be ignored.  Don't set
+    // has_static_relocs for the corresponding symbol.
+    case elfcpp::R_MIPS_JALR:
+    case elfcpp::R_MICROMIPS_JALR:
+      break;
+
+    case elfcpp::R_MIPS_GPREL16:
+    case elfcpp::R_MIPS_GPREL32:
+    case elfcpp::R_MIPS16_GPREL:
+    case elfcpp::R_MICROMIPS_GPREL16:
+      // TODO(sasa)
+      // GP-relative relocations always resolve to a definition in a
+      // regular input file, ignoring the one-definition rule.  This is
+      // important for the GP setup sequence in NewABI code, which
+      // always resolves to a local function even if other relocations
+      // against the symbol wouldn't.
+      //constrain_symbol_p = FALSE;
+      break;
+
+    case elfcpp::R_MIPS_32:
+    case elfcpp::R_MIPS_REL32:
+    case elfcpp::R_MIPS_64:
+      if (parameters->options().shared()
+          || strcmp(gsym->name(), "__gnu_local_gp") != 0)
+        {
+          if (r_type != elfcpp::R_MIPS_REL32)
+            {
+              static_reloc = true;
+              mips_sym->set_pointer_equality_needed();
+            }
+          can_make_dynamic = true;
+          break;
+        }
+      // Fall through.
+
+    default:
+      // Most static relocations require pointer equality, except
+      // for branches.
+      mips_sym->set_pointer_equality_needed();
+
+      // Fall through.
+
+    case elfcpp::R_MIPS_26:
+    case elfcpp::R_MIPS_PC16:
+    case elfcpp::R_MIPS16_26:
+    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:
+      static_reloc = true;
+      mips_sym->set_has_static_relocs();
+      break;
+    }
+
+  // If there are call relocations against an externally-defined symbol,
+  // see whether we can create a MIPS lazy-binding stub for it.  We can
+  // only do this if all references to the function are through call
+  // relocations, and in that case, the traditional lazy-binding stubs
+  // are much more efficient than PLT entries.
+  switch (r_type)
+    {
+    case elfcpp::R_MIPS16_CALL16:
+    case elfcpp::R_MIPS_CALL16:
+    case elfcpp::R_MIPS_CALL_HI16:
+    case elfcpp::R_MIPS_CALL_LO16:
+    case elfcpp::R_MIPS_JALR:
+    case elfcpp::R_MICROMIPS_CALL16:
+    case elfcpp::R_MICROMIPS_CALL_HI16:
+    case elfcpp::R_MICROMIPS_CALL_LO16:
+    case elfcpp::R_MICROMIPS_JALR:
+      if (!mips_sym->no_lazy_stub())
+        {
+          if ((mips_sym->needs_plt_entry() && mips_sym->is_from_dynobj())
+              // Calls from shared objects to undefined symbols of type
+              // STT_NOTYPE need lazy-binding stub.
+              || (mips_sym->is_undefined() && parameters->options().shared()))
+            target->mips_stubs_section(layout)->make_entry(mips_sym);
+        }
+      break;
+    default:
+      {
+        // We must not create a stub for a symbol that has relocations
+        // related to taking the function's address.
+        mips_sym->set_no_lazy_stub();
+        target->remove_lazy_stub_entry(mips_sym);
+        break;
+      }
+  }
+
+  if (relocation_needs_la25_stub<size, big_endian>(mips_obj, r_type,
+                                                   mips_sym->is_mips16()))
+    mips_sym->set_has_nonpic_branches();
+
+  // R_MIPS_HI16 against _gp_disp is used for $gp setup,
+  // and has a special meaning.
+  bool gp_disp_against_hi16 = (!mips_obj->is_newabi()
+                               && strcmp(gsym->name(), "_gp_disp") == 0
+                               && (hi16_reloc(r_type) || lo16_reloc(r_type)));
+  if (static_reloc && gsym->needs_plt_entry())
+    {
+      target->make_plt_entry(symtab, layout, mips_sym, r_type);
+
+      // Since this is not a PC-relative relocation, we may be
+      // taking the address of a function.  In that case we need to
+      // set the entry in the dynamic symbol table to the address of
+      // the PLT entry.
+      if (gsym->is_from_dynobj() && !parameters->options().shared())
+        {
+          gsym->set_needs_dynsym_value();
+          // We distinguish between PLT entries and lazy-binding stubs by
+          // giving the former an st_other value of STO_MIPS_PLT.  Set the
+          // flag if there are any relocations in the binary where pointer
+          // equality matters.
+          if (mips_sym->pointer_equality_needed())
+            mips_sym->set_mips_plt();
+        }
+    }
+  if ((static_reloc || can_make_dynamic) && !gp_disp_against_hi16)
+    {
+      // Absolute addressing relocations.
+      // Make a dynamic relocation if necessary.
+      if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type)))
+        {
+          if (gsym->may_need_copy_reloc())
+            {
+              target->copy_reloc(symtab, layout, object,
+                                 data_shndx, output_section, gsym, *rel);
+            }
+          else if (can_make_dynamic)
+            {
+              // Create .rel.dyn section.
+              target->rel_dyn_section(layout);
+              target->dynamic_reloc(mips_sym, elfcpp::R_MIPS_REL32, mips_obj,
+                                    data_shndx, output_section, r_offset);
+            }
+          else
+            gold_error(_("non-dynamic relocations refer to dynamic symbol %s"),
+                       gsym->name());
+        }
+    }
+
+  bool for_call = false;
+  switch (r_type)
+    {
+    case elfcpp::R_MIPS_CALL16:
+    case elfcpp::R_MIPS16_CALL16:
+    case elfcpp::R_MICROMIPS_CALL16:
+    case elfcpp::R_MIPS_CALL_HI16:
+    case elfcpp::R_MIPS_CALL_LO16:
+    case elfcpp::R_MICROMIPS_CALL_HI16:
+    case elfcpp::R_MICROMIPS_CALL_LO16:
+      for_call = true;
+      // Fall through.
+
+    case elfcpp::R_MIPS16_GOT16:
+    case elfcpp::R_MIPS_GOT16:
+    case elfcpp::R_MIPS_GOT_HI16:
+    case elfcpp::R_MIPS_GOT_LO16:
+    case elfcpp::R_MICROMIPS_GOT16:
+    case elfcpp::R_MICROMIPS_GOT_HI16:
+    case elfcpp::R_MICROMIPS_GOT_LO16:
+    case elfcpp::R_MIPS_GOT_DISP:
+    case elfcpp::R_MICROMIPS_GOT_DISP:
+      {
+        // The symbol requires a GOT entry.
+        Mips_output_data_got<size, big_endian>* got =
+          target->got_section(symtab, layout);
+        got->record_global_got_symbol(mips_sym, mips_obj, r_type, false,
+                                      for_call);
+        mips_sym->set_global_got_area(GGA_NORMAL);
+      }
+      break;
+
+    case elfcpp::R_MIPS_GOT_PAGE:
+    case elfcpp::R_MICROMIPS_GOT_PAGE:
+      {
+        // This relocation needs a page entry in the GOT.
+        // Get the section contents.
+        section_size_type view_size = 0;
+        const unsigned char* view =
+          object->section_contents(data_shndx, &view_size, false);
+        view += r_offset;
+
+        Valtype32 val = elfcpp::Swap<32, big_endian>::readval(view);
+        Valtype32 addend = (rel_type == elfcpp::SHT_REL ? val & 0xffff
+                                                        : r_addend);
+        Mips_output_data_got<size, big_endian>* got =
+          target->got_section(symtab, layout);
+        got->record_got_page_entry(mips_obj, r_sym, addend);
+
+        // If this is a global, overridable symbol, GOT_PAGE will
+        // decay to GOT_DISP, so we'll need a GOT entry for it.
+        bool def_regular = (mips_sym->source() == Symbol::FROM_OBJECT
+                            && !mips_sym->object()->is_dynamic()
+                            && !mips_sym->is_undefined());
+        if (!def_regular
+            || (parameters->options().output_is_position_independent()
+                && !parameters->options().Bsymbolic()
+                && !mips_sym->is_forced_local()))
+          {
+            got->record_global_got_symbol(mips_sym, mips_obj, r_type, false,
+                                          for_call);
+            mips_sym->set_global_got_area(GGA_NORMAL);
+          }
+      }
+      break;
+
+    case elfcpp::R_MIPS_TLS_GOTTPREL:
+    case elfcpp::R_MIPS16_TLS_GOTTPREL:
+    case elfcpp::R_MICROMIPS_TLS_GOTTPREL:
+    case elfcpp::R_MIPS_TLS_LDM:
+    case elfcpp::R_MIPS16_TLS_LDM:
+    case elfcpp::R_MICROMIPS_TLS_LDM:
+    case elfcpp::R_MIPS_TLS_GD:
+    case elfcpp::R_MIPS16_TLS_GD:
+    case elfcpp::R_MICROMIPS_TLS_GD:
+      {
+        const bool is_final = gsym->final_value_is_known();
+        const tls::Tls_optimization optimized_type =
+          Target_mips<size, big_endian>::optimize_tls_reloc(is_final, r_type);
+
+        switch (r_type)
+          {
+          case elfcpp::R_MIPS_TLS_GD:
+          case elfcpp::R_MIPS16_TLS_GD:
+          case elfcpp::R_MICROMIPS_TLS_GD:
+            if (optimized_type == tls::TLSOPT_NONE)
+              {
+                // Create a pair of GOT entries for the module index and
+                // dtv-relative offset.
+                Mips_output_data_got<size, big_endian>* got =
+                  target->got_section(symtab, layout);
+                got->record_global_got_symbol(mips_sym, mips_obj, r_type, false,
+                                              false);
+              }
+            else
+              {
+                // FIXME: TLS optimization not supported yet.
+                gold_unreachable();
+              }
+            break;
+
+          case elfcpp::R_MIPS_TLS_LDM:
+          case elfcpp::R_MIPS16_TLS_LDM:
+          case elfcpp::R_MICROMIPS_TLS_LDM:
+            if (optimized_type == tls::TLSOPT_NONE)
+              {
+                // We always record LDM symbols as local with index 0.
+                target->got_section()->record_local_got_symbol(mips_obj, 0,
+                                                               r_addend, r_type,
+                                                               -1U);
+              }
+            else
+              {
+                // FIXME: TLS optimization not supported yet.
+                gold_unreachable();
+              }
+            break;
+          case elfcpp::R_MIPS_TLS_GOTTPREL:
+          case elfcpp::R_MIPS16_TLS_GOTTPREL:
+          case elfcpp::R_MICROMIPS_TLS_GOTTPREL:
+            layout->set_has_static_tls();
+            if (optimized_type == tls::TLSOPT_NONE)
+              {
+                // Create a GOT entry for the tp-relative offset.
+                Mips_output_data_got<size, big_endian>* got =
+                  target->got_section(symtab, layout);
+                got->record_global_got_symbol(mips_sym, mips_obj, r_type, false,
+                                              false);
+              }
+            else
+              {
+                // FIXME: TLS optimization not supported yet.
+                gold_unreachable();
+              }
+            break;
+
+          default:
+            gold_unreachable();
+        }
+      }
+      break;
+    case elfcpp::R_MIPS_COPY:
+    case elfcpp::R_MIPS_JUMP_SLOT:
+      // These are relocations which should only be seen by the
+      // dynamic linker, and should never be seen here.
+      gold_error(_("%s: unexpected reloc %u in object file"),
+                 object->name().c_str(), r_type);
+      break;
+
+    default:
+      break;
+    }
+
+  // Refuse some position-dependent relocations when creating a
+  // shared library.  Do not refuse R_MIPS_32 / R_MIPS_64; they're
+  // not PIC, but we can create dynamic relocations and the result
+  // will be fine.  Also do not refuse R_MIPS_LO16, which can be
+  // combined with R_MIPS_GOT16.
+  if (parameters->options().shared())
+    {
+      switch (r_type)
+        {
+        case elfcpp::R_MIPS16_HI16:
+        case elfcpp::R_MIPS_HI16:
+        case elfcpp::R_MICROMIPS_HI16:
+          // Don't refuse a high part relocation if it's against
+          // no symbol (e.g. part of a compound relocation).
+          if (r_sym == 0)
+            break;
+
+          // R_MIPS_HI16 against _gp_disp is used for $gp setup,
+          // and has a special meaning.
+          if (!mips_obj->is_newabi() && strcmp(gsym->name(), "_gp_disp") == 0)
+            break;
+
+          // FALLTHROUGH
+
+        case elfcpp::R_MIPS16_26:
+        case elfcpp::R_MIPS_26:
+        case elfcpp::R_MICROMIPS_26_S1:
+          gold_error(_("%s: relocation %u against `%s' can not be used when "
+                       "making a shared object; recompile with -fPIC"),
+                     object->name().c_str(), r_type, gsym->name());
+        default:
+          break;
+        }
+    }
+}
+
+template<int size, bool big_endian>
+inline void
+Target_mips<size, big_endian>::Scan::global(
+                                Symbol_table* symtab,
+                                Layout* layout,
+                                Target_mips<size, big_endian>* 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)
+{
+  global(
+    symtab,
+    layout,
+    target,
+    object,
+    data_shndx,
+    output_section,
+    &reloc,
+    (const elfcpp::Rel<size, big_endian>*) NULL,
+    elfcpp::SHT_RELA,
+    r_type,
+    gsym);
+}
+
+template<int size, bool big_endian>
+inline void
+Target_mips<size, big_endian>::Scan::global(
+                                Symbol_table* symtab,
+                                Layout* layout,
+                                Target_mips<size, big_endian>* 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)
+{
+  global(
+    symtab,
+    layout,
+    target,
+    object,
+    data_shndx,
+    output_section,
+    (const elfcpp::Rela<size, big_endian>*) NULL,
+    &reloc,
+    elfcpp::SHT_REL,
+    r_type,
+    gsym);
+}
+
+// Return whether a R_MIPS_32 relocation needs to be applied.
+
+template<int size, bool big_endian>
+inline bool
+Target_mips<size, big_endian>::Relocate::should_apply_r_mips_32_reloc(
+    const Mips_symbol<size>* gsym,
+    unsigned int r_type,
+    Output_section* output_section,
+    Target_mips* target)
+{
+  // If the output section is not allocated, then we didn't call
+  // scan_relocs, we didn't create a dynamic reloc, and we must apply
+  // the reloc here.
+  if ((output_section->flags() & elfcpp::SHF_ALLOC) == 0)
+      return true;
+
+  if (gsym == NULL)
+    return true;
+  else
+    {
+      // For global symbols, we use the same helper routines used in the
+      // scan pass.
+      if (gsym->needs_dynamic_reloc(Scan::get_reference_flags(r_type))
+          && !gsym->may_need_copy_reloc())
+        {
+          // We have generated dynamic reloc (R_MIPS_REL32).
+
+          bool multi_got = false;
+          if (target->has_got_section())
+            multi_got = target->got_section()->multi_got();
+          bool has_got_offset;
+          if (!multi_got)
+            has_got_offset = gsym->has_got_offset(GOT_TYPE_STANDARD);
+          else
+            has_got_offset = gsym->global_gotoffset() != -1U;
+          if (!has_got_offset)
+            return true;
+          else
+            // Apply the relocation only if the symbol is in the local got.
+            // Do not apply the relocation if the symbol is in the global
+            // got.
+            return symbol_references_local(gsym, gsym->has_dynsym_index());
+        }
+      else
+        // We have not generated dynamic reloc.
+        return true;
+    }
+}
+
+// Perform a relocation.
+
+template<int size, bool big_endian>
+inline bool
+Target_mips<size, big_endian>::Relocate::relocate(
+                        const Relocate_info<size, big_endian>* relinfo,
+                        Target_mips* target,
+                        Output_section* output_section,
+                        size_t relnum,
+                        const elfcpp::Rela<size, big_endian>* rela,
+                        const elfcpp::Rel<size, big_endian>* rel,
+                        unsigned int rel_type,
+                        unsigned int r_type,
+                        const Sized_symbol<size>* gsym,
+                        const Symbol_value<size>* psymval,
+                        unsigned char* view,
+                        Mips_address address,
+                        section_size_type)
+{
+  Mips_address r_offset;
+  typename elfcpp::Elf_types<size>::Elf_WXword r_info;
+  typename elfcpp::Elf_types<size>::Elf_Swxword r_addend;
+
+  if (rel_type == elfcpp::SHT_RELA)
+    {
+      r_offset = rela->get_r_offset();
+      r_info = rela->get_r_info();
+      r_addend = rela->get_r_addend();
+    }
+  else
+    {
+      r_offset = rel->get_r_offset();
+      r_info = rel->get_r_info();
+      r_addend = 0;
+    }
+
+  typedef Mips_relocate_functions<size, big_endian> Reloc_funcs;
+  typename Reloc_funcs::Status reloc_status = Reloc_funcs::STATUS_OKAY;
+
+  Mips_relobj<size, big_endian>* object =
+    Mips_relobj<size, big_endian>::as_mips_relobj(relinfo->object);
+
+  unsigned int r_sym = elfcpp::elf_r_sym<size>(r_info);
+  bool target_is_16_bit_code = false;
+  bool target_is_micromips_code = false;
+  bool cross_mode_jump;
+
+  Symbol_value<size> symval;
+
+  const Mips_symbol<size>* mips_sym = Mips_symbol<size>::as_mips_sym(gsym);
+
+  bool changed_symbol_value = false;
+  if (gsym == NULL)
+    {
+      target_is_16_bit_code = object->local_symbol_is_mips16(r_sym);
+      target_is_micromips_code = object->local_symbol_is_micromips(r_sym);
+      if (target_is_16_bit_code || target_is_micromips_code)
+        {
+          // MIPS16/microMIPS text labels should be treated as odd.
+          symval.set_output_value(psymval->value(object, 1));
+          psymval = &symval;
+          changed_symbol_value = true;
+        }
+    }
+  else
+    {
+      target_is_16_bit_code = mips_sym->is_mips16();
+      target_is_micromips_code = mips_sym->is_micromips();
+
+      // If this is a mips16/microMIPS text symbol, add 1 to the value to make
+      // it odd.  This will cause something like .word SYM to come up with
+      // the right value when it is loaded into the PC.
+
+      if ((mips_sym->is_mips16() || mips_sym->is_micromips())
+          && psymval->value(object, 0) != 0)
+        {
+          symval.set_output_value(psymval->value(object, 0) | 1);
+          psymval = &symval;
+          changed_symbol_value = true;
+        }
+
+      // Pick the value to use for symbols defined in shared objects.
+      if (mips_sym->use_plt_offset(Scan::get_reference_flags(r_type))
+          || mips_sym->has_lazy_stub())
+        {
+          Mips_address value;
+          if (!mips_sym->has_lazy_stub())
+            {
+              // Prefer a standard MIPS PLT entry.
+              if (mips_sym->has_mips_plt_offset())
+                {
+                  value = target->plt_section()->mips_entry_address(mips_sym);
+                  target_is_micromips_code = false;
+                  target_is_16_bit_code = false;
+                }
+              else
+                {
+                  value = (target->plt_section()->comp_entry_address(mips_sym)
+                           + 1);
+                  if (target->is_output_micromips())
+                    target_is_micromips_code = true;
+                  else
+                    target_is_16_bit_code = true;
+                }
+            }
+          else
+            value = target->mips_stubs_section()->stub_address(mips_sym);
+
+          symval.set_output_value(value);
+          psymval = &symval;
+        }
+    }
+
+  // TRUE if the symbol referred to by this relocation is "_gp_disp".
+  // Note that such a symbol must always be a global symbol.
+  bool gp_disp = (gsym != NULL && (strcmp(gsym->name(), "_gp_disp") == 0)
+                  && !object->is_newabi());
+
+  // TRUE if the symbol referred to by this relocation is "__gnu_local_gp".
+  // Note that such a symbol must always be a global symbol.
+  bool gnu_local_gp = gsym && (strcmp(gsym->name(), "__gnu_local_gp") == 0);
+
+
+  if (gp_disp)
+    {
+      if (!hi16_reloc(r_type) && !lo16_reloc(r_type))
+        gold_error_at_location(relinfo, relnum, r_offset,
+          _("relocations against _gp_disp are permitted only"
+            " with R_MIPS_HI16 and R_MIPS_LO16 relocations."));
+    }
+  else if (gnu_local_gp)
+    {
+      // __gnu_local_gp is _gp symbol.
+      symval.set_output_value(target->adjusted_gp_value(object));
+      psymval = &symval;
+    }
+
+  // If this is a reference to a 16-bit function with a stub, we need
+  // to redirect the relocation to the stub unless:
+  //
+  // (a) the relocation is for a MIPS16 JAL;
+  //
+  // (b) the relocation is for a MIPS16 PIC call, and there are no
+  //     non-MIPS16 uses of the GOT slot; or
+  //
+  // (c) the section allows direct references to MIPS16 functions.
+  if (r_type != elfcpp::R_MIPS16_26
+      && !parameters->options().relocatable()
+      && ((mips_sym != NULL
+           && mips_sym->has_mips16_fn_stub()
+           && (r_type != elfcpp::R_MIPS16_CALL16 || mips_sym->need_fn_stub()))
+          || (mips_sym == NULL
+              && object->get_local_mips16_fn_stub(r_sym) != NULL))
+      && !object->section_allows_mips16_refs(relinfo->data_shndx))
+    {
+      // This is a 32- or 64-bit call to a 16-bit function.  We should
+      // have already noticed that we were going to need the
+      // stub.
+      Mips_address value;
+      if (mips_sym == NULL)
+        value = object->get_local_mips16_fn_stub(r_sym)->output_address();
+      else
+        {
+          gold_assert(mips_sym->need_fn_stub());
+          if (mips_sym->has_la25_stub())
+            value = target->la25_stub_section()->stub_address(mips_sym);
+          else
+            {
+              value = mips_sym->template
+                      get_mips16_fn_stub<big_endian>()->output_address();
+            }
+          }
+      symval.set_output_value(value);
+      psymval = &symval;
+      changed_symbol_value = true;
+
+      // The target is 16-bit, but the stub isn't.
+      target_is_16_bit_code = false;
+    }
+  // If this is a MIPS16 call with a stub, that is made through the PLT or
+  // to a standard MIPS function, we need to redirect the call to the stub.
+  // Note that we specifically exclude R_MIPS16_CALL16 from this behavior;
+  // indirect calls should use an indirect stub instead.
+  else if (r_type == elfcpp::R_MIPS16_26 && !parameters->options().relocatable()
+           && ((mips_sym != NULL
+                && (mips_sym->has_mips16_call_stub()
+                    || mips_sym->has_mips16_call_fp_stub()))
+               || (mips_sym == NULL
+                   && object->get_local_mips16_call_stub(r_sym) != NULL))
+           && ((mips_sym != NULL && mips_sym->has_plt_offset())
+               || !target_is_16_bit_code))
+    {
+      Mips16_stub_section<size, big_endian>* call_stub;
+      if (mips_sym == NULL)
+        call_stub = object->get_local_mips16_call_stub(r_sym);
+      else
+        {
+          // If both call_stub and call_fp_stub are defined, we can figure
+          // out which one to use by checking which one appears in the input
+          // file.
+          if (mips_sym->has_mips16_call_stub()
+              && mips_sym->has_mips16_call_fp_stub())
+            {
+              call_stub = NULL;
+              for (unsigned int i = 1; i < object->shnum(); ++i)
+                {
+                  if (object->is_mips16_call_fp_stub_section(i))
+                    {
+                      call_stub = mips_sym->template
+                                  get_mips16_call_fp_stub<big_endian>();
+                      break;
+                    }
+
+                }
+              if (call_stub == NULL)
+                call_stub =
+                  mips_sym->template get_mips16_call_stub<big_endian>();
+            }
+          else if (mips_sym->has_mips16_call_stub())
+            call_stub = mips_sym->template get_mips16_call_stub<big_endian>();
+          else
+            call_stub = mips_sym->template get_mips16_call_fp_stub<big_endian>();
+        }
+
+      symval.set_output_value(call_stub->output_address());
+      psymval = &symval;
+      changed_symbol_value = true;
+    }
+  // If this is a direct call to a PIC function, redirect to the
+  // non-PIC stub.
+  else if (mips_sym != NULL
+           && mips_sym->has_la25_stub()
+           && relocation_needs_la25_stub<size, big_endian>(
+                                       object, r_type, target_is_16_bit_code))
+    {
+      Mips_address value = target->la25_stub_section()->stub_address(mips_sym);
+      if (mips_sym->is_micromips())
+        value += 1;
+      symval.set_output_value(value);
+      psymval = &symval;
+    }
+  // For direct MIPS16 and microMIPS calls make sure the compressed PLT
+  // entry is used if a standard PLT entry has also been made.
+  else if ((r_type == elfcpp::R_MIPS16_26
+            || r_type == elfcpp::R_MICROMIPS_26_S1)
+          && !parameters->options().relocatable()
+          && mips_sym != NULL
+          && mips_sym->has_plt_offset()
+          && mips_sym->has_comp_plt_offset()
+          && mips_sym->has_mips_plt_offset())
+    {
+      Mips_address value = (target->plt_section()->comp_entry_address(mips_sym)
+                            + 1);
+      symval.set_output_value(value);
+      psymval = &symval;
+
+      target_is_16_bit_code = !target->is_output_micromips();
+      target_is_micromips_code = target->is_output_micromips();
+    }
+
+  // Make sure MIPS16 and microMIPS are not used together.
+  if ((r_type == elfcpp::R_MIPS16_26 && target_is_micromips_code)
+      || (micromips_branch_reloc(r_type) && target_is_16_bit_code))
+   {
+      gold_error(_("MIPS16 and microMIPS functions cannot call each other"));
+   }
+
+  // Calls from 16-bit code to 32-bit code and vice versa require the
+  // mode change.  However, we can ignore calls to undefined weak symbols,
+  // which should never be executed at runtime.  This exception is important
+  // because the assembly writer may have "known" that any definition of the
+  // symbol would be 16-bit code, and that direct jumps were therefore
+  // acceptable.
+  cross_mode_jump =
+    (!parameters->options().relocatable()
+     && !(gsym != NULL && gsym->is_weak_undefined())
+     && ((r_type == elfcpp::R_MIPS16_26 && !target_is_16_bit_code)
+         || (r_type == elfcpp::R_MICROMIPS_26_S1 && !target_is_micromips_code)
+         || ((r_type == elfcpp::R_MIPS_26 || r_type == elfcpp::R_MIPS_JALR)
+             && (target_is_16_bit_code || target_is_micromips_code))));
+
+  bool local = (mips_sym == NULL
+                || (mips_sym->got_only_for_calls()
+                    ? symbol_calls_local(mips_sym, mips_sym->has_dynsym_index())
+                    : symbol_references_local(mips_sym,
+                                              mips_sym->has_dynsym_index())));
+
+  // Global R_MIPS_GOT_PAGE/R_MICROMIPS_GOT_PAGE relocations are equivalent
+  // to R_MIPS_GOT_DISP/R_MICROMIPS_GOT_DISP.  The addend is applied by the
+  // corresponding R_MIPS_GOT_OFST/R_MICROMIPS_GOT_OFST.
+  if (got_page_reloc(r_type) && !local)
+    r_type = (micromips_reloc(r_type) ? elfcpp::R_MICROMIPS_GOT_DISP
+                                      : elfcpp::R_MIPS_GOT_DISP);
+
+  unsigned int got_offset = 0;
+  int gp_offset = 0;
+
+  bool update_got_entry = false;
+  bool extract_addend = rel_type == elfcpp::SHT_REL;
+  switch (r_type)
+    {
+    case elfcpp::R_MIPS_NONE:
+      break;
+    case elfcpp::R_MIPS_16:
+      reloc_status = Reloc_funcs::rel16(view, object, psymval, r_addend,
+                                        extract_addend, r_type);
+      break;
+
+    case elfcpp::R_MIPS_32:
+      if (should_apply_r_mips_32_reloc(mips_sym, r_type, output_section,
+                                       target))
+        reloc_status = Reloc_funcs::rel32(view, object, psymval, r_addend,
+                                          extract_addend, r_type);
+      if (mips_sym != NULL
+          && (mips_sym->is_mips16() || mips_sym->is_micromips())
+          && mips_sym->global_got_area() == GGA_RELOC_ONLY)
+        {
+          // If mips_sym->has_mips16_fn_stub() is false, symbol value is
+          // already updated by adding +1.
+          if (mips_sym->has_mips16_fn_stub())
+            {
+              gold_assert(mips_sym->need_fn_stub());
+              Mips16_stub_section<size, big_endian>* fn_stub =
+                mips_sym->template get_mips16_fn_stub<big_endian>();
+
+              symval.set_output_value(fn_stub->output_address());
+              psymval = &symval;
+            }
+          got_offset = mips_sym->global_gotoffset();
+          update_got_entry = true;
+        }
+      break;
+
+    case elfcpp::R_MIPS_REL32:
+      gold_unreachable();
+
+    case elfcpp::R_MIPS_PC32:
+      reloc_status = Reloc_funcs::relpc32(view, object, psymval, address,
+                                          r_addend, extract_addend, r_type);
+      break;
+
+    case elfcpp::R_MIPS16_26:
+      // The calculation for R_MIPS16_26 is just the same as for an
+      // R_MIPS_26.  It's only the storage of the relocated field into
+      // the output file that's different.  So, we just fall through to the
+      // R_MIPS_26 case here.
+    case elfcpp::R_MIPS_26:
+    case elfcpp::R_MICROMIPS_26_S1:
+      reloc_status = Reloc_funcs::rel26(view, object, psymval, address,
+          gsym == NULL, r_addend, extract_addend, gsym, cross_mode_jump, r_type,
+          target->jal_to_bal());
+      break;
+
+    case elfcpp::R_MIPS_HI16:
+    case elfcpp::R_MIPS16_HI16:
+    case elfcpp::R_MICROMIPS_HI16:
+      reloc_status = Reloc_funcs::relhi16(view, object, psymval, r_addend,
+                                          address, gp_disp, r_type,
+                                          extract_addend);
+      break;
+
+    case elfcpp::R_MIPS_LO16:
+    case elfcpp::R_MIPS16_LO16:
+    case elfcpp::R_MICROMIPS_LO16:
+    case elfcpp::R_MICROMIPS_HI0_LO16:
+      reloc_status = Reloc_funcs::rello16(target, view, object, psymval,
+                                          r_addend, extract_addend, address,
+                                          gp_disp, r_type);
+      break;
+
+    case elfcpp::R_MIPS_LITERAL:
+    case elfcpp::R_MICROMIPS_LITERAL:
+      // Because we don't merge literal sections, we can handle this
+      // just like R_MIPS_GPREL16.  In the long run, we should merge
+      // shared literals, and then we will need to additional work
+      // here.
+
+      // Fall through.
+
+    case elfcpp::R_MIPS_GPREL16:
+    case elfcpp::R_MIPS16_GPREL:
+    case elfcpp::R_MICROMIPS_GPREL7_S2:
+    case elfcpp::R_MICROMIPS_GPREL16:
+      reloc_status = Reloc_funcs::relgprel(view, object, psymval,
+                                           target->adjusted_gp_value(object),
+                                           r_addend, extract_addend,
+                                           gsym == NULL, r_type);
+      break;
+
+    case elfcpp::R_MIPS_PC16:
+      reloc_status = Reloc_funcs::relpc16(view, object, psymval, address,
+                                          r_addend, extract_addend, r_type);
+      break;
+    case elfcpp::R_MICROMIPS_PC7_S1:
+      reloc_status = Reloc_funcs::relmicromips_pc7_s1(view, object, psymval,
+                                                      address, r_addend,
+                                                      extract_addend, r_type);
+      break;
+    case elfcpp::R_MICROMIPS_PC10_S1:
+      reloc_status = Reloc_funcs::relmicromips_pc10_s1(view, object, psymval,
+                                                       address, r_addend,
+                                                       extract_addend, r_type);
+      break;
+    case elfcpp::R_MICROMIPS_PC16_S1:
+      reloc_status = Reloc_funcs::relmicromips_pc16_s1(view, object, psymval,
+                                                       address, r_addend,
+                                                       extract_addend, r_type);
+      break;
+    case elfcpp::R_MIPS_GPREL32:
+      reloc_status = Reloc_funcs::relgprel32(view, object, psymval,
+                                             target->adjusted_gp_value(object),
+                                             r_addend, extract_addend, r_type);
+      break;
+    case elfcpp::R_MIPS_GOT_HI16:
+    case elfcpp::R_MIPS_CALL_HI16:
+    case elfcpp::R_MICROMIPS_GOT_HI16:
+    case elfcpp::R_MICROMIPS_CALL_HI16:
+      if (gsym != NULL)
+        got_offset = target->got_section()->got_offset(gsym, GOT_TYPE_STANDARD,
+                                                       object);
+      else
+        got_offset = target->got_section()->got_offset(r_sym, GOT_TYPE_STANDARD,
+                                                       object);
+      gp_offset = target->got_section()->gp_offset(got_offset, object);
+      reloc_status = Reloc_funcs::relgot_hi16(view, gp_offset, r_type);
+      update_got_entry = changed_symbol_value;
+      break;
+
+    case elfcpp::R_MIPS_GOT_LO16:
+    case elfcpp::R_MIPS_CALL_LO16:
+    case elfcpp::R_MICROMIPS_GOT_LO16:
+    case elfcpp::R_MICROMIPS_CALL_LO16:
+      if (gsym != NULL)
+        got_offset = target->got_section()->got_offset(gsym, GOT_TYPE_STANDARD,
+                                                       object);
+      else
+        got_offset = target->got_section()->got_offset(r_sym, GOT_TYPE_STANDARD,
+                                                       object);
+      gp_offset = target->got_section()->gp_offset(got_offset, object);
+      reloc_status = Reloc_funcs::relgot_lo16(view, gp_offset, r_type);
+      update_got_entry = changed_symbol_value;
+      break;
+
+    case elfcpp::R_MIPS_GOT_DISP:
+    case elfcpp::R_MICROMIPS_GOT_DISP:
+      if (gsym != NULL)
+        got_offset = target->got_section()->got_offset(gsym, GOT_TYPE_STANDARD,
+                                                       object);
+      else
+        got_offset = target->got_section()->got_offset(r_sym, GOT_TYPE_STANDARD,
+                                                       object);
+      gp_offset = target->got_section()->gp_offset(got_offset, object);
+      reloc_status = Reloc_funcs::relgot(view, gp_offset, r_type);
+      break;
+
+    case elfcpp::R_MIPS_CALL16:
+    case elfcpp::R_MIPS16_CALL16:
+    case elfcpp::R_MICROMIPS_CALL16:
+      gold_assert(gsym != NULL);
+      got_offset = target->got_section()->got_offset(gsym, GOT_TYPE_STANDARD,
+                                                     object);
+      gp_offset = target->got_section()->gp_offset(got_offset, object);
+      reloc_status = Reloc_funcs::relgot(view, gp_offset, r_type);
+      // TODO(sasa): We should also initialize update_got_entry in other places
+      // where relgot is called.
+      update_got_entry = changed_symbol_value;
+      break;
+
+    case elfcpp::R_MIPS_GOT16:
+    case elfcpp::R_MIPS16_GOT16:
+    case elfcpp::R_MICROMIPS_GOT16:
+      if (gsym != NULL)
+        {
+          got_offset = target->got_section()->got_offset(gsym,
+                                                         GOT_TYPE_STANDARD,
+                                                         object);
+          gp_offset = target->got_section()->gp_offset(got_offset, object);
+          reloc_status = Reloc_funcs::relgot(view, gp_offset, r_type);
+        }
+      else
+        reloc_status = Reloc_funcs::relgot16_local(view, object, psymval,
+                                                   r_addend, extract_addend,
+                                                   r_type);
+      update_got_entry = changed_symbol_value;
+      break;
+
+    case elfcpp::R_MIPS_TLS_GD:
+    case elfcpp::R_MIPS16_TLS_GD:
+    case elfcpp::R_MICROMIPS_TLS_GD:
+      if (gsym != NULL)
+        got_offset = target->got_section()->got_offset(gsym, GOT_TYPE_TLS_PAIR,
+                                                       object);
+      else
+        got_offset = target->got_section()->got_offset(r_sym, GOT_TYPE_TLS_PAIR,
+                                                       object);
+      gp_offset = target->got_section()->gp_offset(got_offset, object);
+      reloc_status = Reloc_funcs::relgot(view, gp_offset, r_type);
+      break;
+
+    case elfcpp::R_MIPS_TLS_GOTTPREL:
+    case elfcpp::R_MIPS16_TLS_GOTTPREL:
+    case elfcpp::R_MICROMIPS_TLS_GOTTPREL:
+      if (gsym != NULL)
+        got_offset = target->got_section()->got_offset(gsym,
+                                                       GOT_TYPE_TLS_OFFSET,
+                                                       object);
+      else
+        got_offset = target->got_section()->got_offset(r_sym,
+                                                       GOT_TYPE_TLS_OFFSET,
+                                                       object);
+      gp_offset = target->got_section()->gp_offset(got_offset, object);
+      reloc_status = Reloc_funcs::relgot(view, gp_offset, r_type);
+      break;
+
+    case elfcpp::R_MIPS_TLS_LDM:
+    case elfcpp::R_MIPS16_TLS_LDM:
+    case elfcpp::R_MICROMIPS_TLS_LDM:
+      // Relocate the field with the offset of the GOT entry for
+      // the module index.
+      got_offset = target->got_section()->tls_ldm_offset(object);
+      gp_offset = target->got_section()->gp_offset(got_offset, object);
+      reloc_status = Reloc_funcs::relgot(view, gp_offset, r_type);
+      break;
+
+    case elfcpp::R_MIPS_GOT_PAGE:
+    case elfcpp::R_MICROMIPS_GOT_PAGE:
+      reloc_status = Reloc_funcs::relgotpage(target, view, object, psymval,
+                                             r_addend, extract_addend, r_type);
+      break;
+
+    case elfcpp::R_MIPS_GOT_OFST:
+    case elfcpp::R_MICROMIPS_GOT_OFST:
+      reloc_status = Reloc_funcs::relgotofst(target, view, object, psymval,
+                                             r_addend, extract_addend, local,
+                                             r_type);
+      break;
+
+    case elfcpp::R_MIPS_JALR:
+    case elfcpp::R_MICROMIPS_JALR:
+      // This relocation is only a hint.  In some cases, we optimize
+      // it into a bal instruction.  But we don't try to optimize
+      // when the symbol does not resolve locally.
+      if (gsym == NULL || symbol_calls_local(gsym, gsym->has_dynsym_index()))
+        reloc_status = Reloc_funcs::reljalr(view, object, psymval, address,
+                                            r_addend, extract_addend,
+                                            cross_mode_jump, r_type,
+                                            target->jalr_to_bal(),
+                                            target->jr_to_b());
+      break;
+
+    case elfcpp::R_MIPS_TLS_DTPREL_HI16:
+    case elfcpp::R_MIPS16_TLS_DTPREL_HI16:
+    case elfcpp::R_MICROMIPS_TLS_DTPREL_HI16:
+      reloc_status = Reloc_funcs::tlsrelhi16(view, object, psymval,
+                                             elfcpp::DTP_OFFSET, r_addend,
+                                             extract_addend, r_type);
+      break;
+    case elfcpp::R_MIPS_TLS_DTPREL_LO16:
+    case elfcpp::R_MIPS16_TLS_DTPREL_LO16:
+    case elfcpp::R_MICROMIPS_TLS_DTPREL_LO16:
+      reloc_status = Reloc_funcs::tlsrello16(view, object, psymval,
+                                             elfcpp::DTP_OFFSET, r_addend,
+                                             extract_addend, r_type);
+      break;
+    case elfcpp::R_MIPS_TLS_DTPREL32:
+    case elfcpp::R_MIPS_TLS_DTPREL64:
+      reloc_status = Reloc_funcs::tlsrel32(view, object, psymval,
+                                           elfcpp::DTP_OFFSET, r_addend,
+                                           extract_addend, r_type);
+      break;
+    case elfcpp::R_MIPS_TLS_TPREL_HI16:
+    case elfcpp::R_MIPS16_TLS_TPREL_HI16:
+    case elfcpp::R_MICROMIPS_TLS_TPREL_HI16:
+      reloc_status = Reloc_funcs::tlsrelhi16(view, object, psymval,
+                                             elfcpp::TP_OFFSET, r_addend,
+                                             extract_addend, r_type);
+      break;
+    case elfcpp::R_MIPS_TLS_TPREL_LO16:
+    case elfcpp::R_MIPS16_TLS_TPREL_LO16:
+    case elfcpp::R_MICROMIPS_TLS_TPREL_LO16:
+      reloc_status = Reloc_funcs::tlsrello16(view, object, psymval,
+                                             elfcpp::TP_OFFSET, r_addend,
+                                             extract_addend, r_type);
+      break;
+    case elfcpp::R_MIPS_TLS_TPREL32:
+    case elfcpp::R_MIPS_TLS_TPREL64:
+      reloc_status = Reloc_funcs::tlsrel32(view, object, psymval,
+                                           elfcpp::TP_OFFSET, r_addend,
+                                           extract_addend, r_type);
+      break;
+    case elfcpp::R_MIPS_SUB:
+    case elfcpp::R_MICROMIPS_SUB:
+      reloc_status = Reloc_funcs::relsub(view, object, psymval, r_addend,
+                                         extract_addend, r_type);
+      break;
+    default:
+      gold_error_at_location(relinfo, relnum, r_offset,
+                             _("unsupported reloc %u"), r_type);
+      break;
+    }
+
+  if (update_got_entry)
+    {
+      Mips_output_data_got<size, big_endian>* got = target->got_section();
+      if (mips_sym != NULL && mips_sym->get_applied_secondary_got_fixup())
+        got->update_got_entry(got->get_primary_got_offset(mips_sym),
+                              psymval->value(object, 0));
+      else
+        got->update_got_entry(got_offset, psymval->value(object, 0));
+    }
+
+  // Report any errors.
+  switch (reloc_status)
+    {
+    case Reloc_funcs::STATUS_OKAY:
+      break;
+    case Reloc_funcs::STATUS_OVERFLOW:
+      gold_error_at_location(relinfo, relnum, r_offset,
+                             _("relocation overflow"));
+      break;
+    case Reloc_funcs::STATUS_BAD_RELOC:
+      gold_error_at_location(relinfo, relnum, r_offset,
+        _("unexpected opcode while processing relocation"));
+      break;
+    default:
+      gold_unreachable();
+    }
+
+  return true;
+}
+
+template<int size, bool big_endian>
+inline bool
+Target_mips<size, big_endian>::Relocate::relocate(
+                        const Relocate_info<size, big_endian>* relinfo,
+                        Target_mips* target,
+                        Output_section* output_section,
+                        size_t relnum,
+                        const elfcpp::Rela<size, big_endian>& reloc,
+                        unsigned int r_type,
+                        const Sized_symbol<size>* gsym,
+                        const Symbol_value<size>* psymval,
+                        unsigned char* view,
+                        Mips_address address,
+                        section_size_type view_size)
+{
+  return relocate(
+    relinfo,
+    target,
+    output_section,
+    relnum,
+    &reloc,
+    (const elfcpp::Rel<size, big_endian>*) NULL,
+    elfcpp::SHT_RELA,
+    r_type,
+    gsym,
+    psymval,
+    view,
+    address,
+    view_size);
+}
+
+template<int size, bool big_endian>
+inline bool
+Target_mips<size, big_endian>::Relocate::relocate(
+                        const Relocate_info<size, big_endian>* relinfo,
+                        Target_mips* target,
+                        Output_section* output_section,
+                        size_t relnum,
+                        const elfcpp::Rel<size, big_endian>& reloc,
+                        unsigned int r_type,
+                        const Sized_symbol<size>* gsym,
+                        const Symbol_value<size>* psymval,
+                        unsigned char* view,
+                        Mips_address address,
+                        section_size_type view_size)
+{
+  return relocate(
+    relinfo,
+    target,
+    output_section,
+    relnum,
+    (const elfcpp::Rela<size, big_endian>*) NULL,
+    &reloc,
+    elfcpp::SHT_REL,
+    r_type,
+    gsym,
+    psymval,
+    view,
+    address,
+    view_size);
+}
+
+// Get the Reference_flags for a particular relocation.
+
+template<int size, bool big_endian>
+int
+Target_mips<size, big_endian>::Scan::get_reference_flags(
+                       unsigned int r_type)
+{
+  switch (r_type)
+    {
+    case elfcpp::R_MIPS_NONE:
+      // No symbol reference.
+      return 0;
+
+    case elfcpp::R_MIPS_16:
+    case elfcpp::R_MIPS_32:
+    case elfcpp::R_MIPS_64:
+    case elfcpp::R_MIPS_HI16:
+    case elfcpp::R_MIPS_LO16:
+    case elfcpp::R_MIPS16_HI16:
+    case elfcpp::R_MIPS16_LO16:
+    case elfcpp::R_MICROMIPS_HI16:
+    case elfcpp::R_MICROMIPS_LO16:
+      return Symbol::ABSOLUTE_REF;
+
+    case elfcpp::R_MIPS_26:
+    case elfcpp::R_MIPS16_26:
+    case elfcpp::R_MICROMIPS_26_S1:
+      return Symbol::FUNCTION_CALL | Symbol::ABSOLUTE_REF;
+
+    case elfcpp::R_MIPS_GPREL32:
+    case elfcpp::R_MIPS_GPREL16:
+    case elfcpp::R_MIPS_REL32:
+    case elfcpp::R_MIPS16_GPREL:
+      return Symbol::RELATIVE_REF;
+
+    case elfcpp::R_MIPS_PC16:
+    case elfcpp::R_MIPS_PC32:
+    case elfcpp::R_MIPS_JALR:
+    case elfcpp::R_MICROMIPS_JALR:
+      return Symbol::FUNCTION_CALL | Symbol::RELATIVE_REF;
+
+    case elfcpp::R_MIPS_GOT16:
+    case elfcpp::R_MIPS_CALL16:
+    case elfcpp::R_MIPS_GOT_DISP:
+    case elfcpp::R_MIPS_GOT_HI16:
+    case elfcpp::R_MIPS_GOT_LO16:
+    case elfcpp::R_MIPS_CALL_HI16:
+    case elfcpp::R_MIPS_CALL_LO16:
+    case elfcpp::R_MIPS_LITERAL:
+    case elfcpp::R_MIPS_GOT_PAGE:
+    case elfcpp::R_MIPS_GOT_OFST:
+    case elfcpp::R_MIPS16_GOT16:
+    case elfcpp::R_MIPS16_CALL16:
+    case elfcpp::R_MICROMIPS_GOT16:
+    case elfcpp::R_MICROMIPS_CALL16:
+    case elfcpp::R_MICROMIPS_GOT_HI16:
+    case elfcpp::R_MICROMIPS_GOT_LO16:
+    case elfcpp::R_MICROMIPS_CALL_HI16:
+    case elfcpp::R_MICROMIPS_CALL_LO16:
+      // Absolute in GOT.
+      return Symbol::RELATIVE_REF;
+
+    case elfcpp::R_MIPS_TLS_DTPMOD32:
+    case elfcpp::R_MIPS_TLS_DTPREL32:
+    case elfcpp::R_MIPS_TLS_DTPMOD64:
+    case elfcpp::R_MIPS_TLS_DTPREL64:
+    case elfcpp::R_MIPS_TLS_GD:
+    case elfcpp::R_MIPS_TLS_LDM:
+    case elfcpp::R_MIPS_TLS_DTPREL_HI16:
+    case elfcpp::R_MIPS_TLS_DTPREL_LO16:
+    case elfcpp::R_MIPS_TLS_GOTTPREL:
+    case elfcpp::R_MIPS_TLS_TPREL32:
+    case elfcpp::R_MIPS_TLS_TPREL64:
+    case elfcpp::R_MIPS_TLS_TPREL_HI16:
+    case elfcpp::R_MIPS_TLS_TPREL_LO16:
+    case elfcpp::R_MIPS16_TLS_GD:
+    case elfcpp::R_MIPS16_TLS_GOTTPREL:
+    case elfcpp::R_MICROMIPS_TLS_GD:
+    case elfcpp::R_MICROMIPS_TLS_GOTTPREL:
+    case elfcpp::R_MICROMIPS_TLS_TPREL_HI16:
+    case elfcpp::R_MICROMIPS_TLS_TPREL_LO16:
+      return Symbol::TLS_REF;
+
+    case elfcpp::R_MIPS_COPY:
+    case elfcpp::R_MIPS_JUMP_SLOT:
+    default:
+      gold_unreachable();
+      // Not expected.  We will give an error later.
+      return 0;
+    }
+}
+
+// Report an unsupported relocation against a local symbol.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::Scan::unsupported_reloc_local(
+                        Sized_relobj_file<size, big_endian>* object,
+                        unsigned int r_type)
+{
+  gold_error(_("%s: unsupported reloc %u against local symbol"),
+             object->name().c_str(), r_type);
+}
+
+// Report an unsupported relocation against a global symbol.
+
+template<int size, bool big_endian>
+void
+Target_mips<size, big_endian>::Scan::unsupported_reloc_global(
+                        Sized_relobj_file<size, big_endian>* object,
+                        unsigned int r_type,
+                        Symbol* gsym)
+{
+  gold_error(_("%s: unsupported reloc %u against global symbol %s"),
+             object->name().c_str(), r_type, gsym->demangled_name().c_str());
+}
+
+// Return printable name for ABI.
+template<int size, bool big_endian>
+const char*
+Target_mips<size, big_endian>::elf_mips_abi_name(elfcpp::Elf_Word e_flags,
+                                                 unsigned char ei_class)
+{
+  switch (e_flags & elfcpp::EF_MIPS_ABI)
+    {
+    case 0:
+      if ((e_flags & elfcpp::EF_MIPS_ABI2) != 0)
+        return "N32";
+      else if (elfcpp::abi_64(ei_class))
+        return "64";
+      else
+        return "none";
+    case elfcpp::E_MIPS_ABI_O32:
+      return "O32";
+    case elfcpp::E_MIPS_ABI_O64:
+      return "O64";
+    case elfcpp::E_MIPS_ABI_EABI32:
+      return "EABI32";
+    case elfcpp::E_MIPS_ABI_EABI64:
+      return "EABI64";
+    default:
+      return "unknown abi";
+    }
+}
+
+template<int size, bool big_endian>
+const char*
+Target_mips<size, big_endian>::elf_mips_mach_name(elfcpp::Elf_Word e_flags)
+{
+  switch (e_flags & elfcpp::EF_MIPS_MACH)
+    {
+    case elfcpp::E_MIPS_MACH_3900:
+      return "mips:3900";
+    case elfcpp::E_MIPS_MACH_4010:
+      return "mips:4010";
+    case elfcpp::E_MIPS_MACH_4100:
+      return "mips:4100";
+    case elfcpp::E_MIPS_MACH_4111:
+      return "mips:4111";
+    case elfcpp::E_MIPS_MACH_4120:
+      return "mips:4120";
+    case elfcpp::E_MIPS_MACH_4650:
+      return "mips:4650";
+    case elfcpp::E_MIPS_MACH_5400:
+      return "mips:5400";
+    case elfcpp::E_MIPS_MACH_5500:
+      return "mips:5500";
+    case elfcpp::E_MIPS_MACH_SB1:
+      return "mips:sb1";
+    case elfcpp::E_MIPS_MACH_9000:
+      return "mips:9000";
+    case elfcpp::E_MIPS_MACH_LS2E:
+      return "mips:loongson-2e";
+    case elfcpp::E_MIPS_MACH_LS2F:
+      return "mips:loongson-2f";
+    case elfcpp::E_MIPS_MACH_LS3A:
+      return "mips:loongson-3a";
+    case elfcpp::E_MIPS_MACH_OCTEON:
+      return "mips:octeon";
+    case elfcpp::E_MIPS_MACH_OCTEON2:
+      return "mips:octeon2";
+    case elfcpp::E_MIPS_MACH_XLR:
+      return "mips:xlr";
+    default:
+      switch (e_flags & elfcpp::EF_MIPS_ARCH)
+        {
+        default:
+        case elfcpp::E_MIPS_ARCH_1:
+          return "mips:3000";
+
+        case elfcpp::E_MIPS_ARCH_2:
+          return "mips:6000";
+
+        case elfcpp::E_MIPS_ARCH_3:
+          return "mips:4000";
+
+        case elfcpp::E_MIPS_ARCH_4:
+          return "mips:8000";
+
+        case elfcpp::E_MIPS_ARCH_5:
+          return "mips:mips5";
+
+        case elfcpp::E_MIPS_ARCH_32:
+          return "mips:isa32";
+
+        case elfcpp::E_MIPS_ARCH_64:
+          return "mips:isa64";
+
+        case elfcpp::E_MIPS_ARCH_32R2:
+          return "mips:isa32r2";
+
+        case elfcpp::E_MIPS_ARCH_64R2:
+          return "mips:isa64r2";
+        }
+    }
+    return "unknown CPU";
+}
+
+template<int size, bool big_endian>
+Target::Target_info Target_mips<size, big_endian>::mips_info =
+{
+  size,                 // size
+  big_endian,           // is_big_endian
+  elfcpp::EM_MIPS,      // machine_code
+  true,                 // has_make_symbol
+  false,                // has_resolve
+  false,                // has_code_fill
+  true,                 // is_default_stack_executable
+  false,                // can_icf_inline_merge_sections
+  '\0',                 // wrap_char
+  "/lib/ld.so.1",       // dynamic_linker
+  0x400000,             // default_text_segment_address
+  64 * 1024,            // abi_pagesize (overridable by -z max-page-size)
+  4 * 1024,             // common_pagesize (overridable by -z common-page-size)
+  false,                // isolate_execinstr
+  0,                    // rosegment_gap
+  elfcpp::SHN_UNDEF,    // small_common_shndx
+  elfcpp::SHN_UNDEF,    // large_common_shndx
+  0,                    // small_common_section_flags
+  0,                    // large_common_section_flags
+  NULL,                 // attributes_section
+  NULL,                 // attributes_vendor
+  "__start"		// entry_symbol_name
+};
+
+// The selector for mips object files.
+
+template<int size, bool big_endian>
+class Target_selector_mips : public Target_selector
+{
+public:
+  Target_selector_mips()
+    : Target_selector(elfcpp::EM_MIPS, size, big_endian,
+                (size == 64 ?
+                  (big_endian ? "elf64-tradbigmips" : "elf64-tradlittlemips") :
+                  (big_endian ? "elf32-tradbigmips" : "elf32-tradlittlemips")),
+                (size == 64 ?
+                  (big_endian ? "elf64-tradbigmips" : "elf64-tradlittlemips") :
+                  (big_endian ? "elf32-tradbigmips" : "elf32-tradlittlemips")))
+  { }
+
+  Target* do_instantiate_target()
+  { return new Target_mips<size, big_endian>(); }
+};
+
+Target_selector_mips<32, true> target_selector_mips32be;
+Target_selector_mips<32, false> target_selector_mips32;
+Target_selector_mips<64, true> target_selector_mips64be;
+Target_selector_mips<64, false> target_selector_mips64;
+
+
+} // End anonymous namespace.

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