]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gold/aarch64.cc
"backtrace full/no-filters/hide" completer
[thirdparty/binutils-gdb.git] / gold / aarch64.cc
index 7309ded676a32c86559eabb563929a8ac2fe0c14..a45d3fd6671985332d7b77ab02846e8761435684 100644 (file)
@@ -1,6 +1,6 @@
 // aarch64.cc -- aarch64 target support for gold.
 
-// Copyright (C) 2014-2017 Free Software Foundation, Inc.
+// Copyright (C) 2014-2019 Free Software Foundation, Inc.
 // Written by Jing Yu <jingyu@google.com> and Han Shen <shenhan@google.com>.
 
 // This file is part of gold.
@@ -270,7 +270,7 @@ public:
     uint32_t v = 0;
     uint32_t opc_v = 0;
 
-    /* Bail out quickly if INSN doesn't fall into the the load-store
+    /* Bail out quickly if INSN doesn't fall into the load-store
        encoding space.  */
     if (!aarch64_ldst (insn))
       return false;
@@ -930,7 +930,7 @@ private:
 
 // Erratum stub class. An erratum stub differs from a reloc stub in that for
 // each erratum occurrence, we generate an erratum stub. We never share erratum
-// stubs, whereas for reloc stubs, different branches insns share a single reloc
+// stubs, whereas for reloc stubs, different branch insns share a single reloc
 // stub as long as the branch targets are the same. (More to the point, reloc
 // stubs can be shared because they're used to reach a specific target, whereas
 // erratum stubs branch back to the original control flow.)
@@ -1031,6 +1031,18 @@ public:
   set_erratum_address(AArch64_address addr)
   { this->erratum_address_ = addr; }
 
+  // Later relaxation passes of may alter the recorded erratum and destination
+  // address. Given an up to date output section address of shidx_ in
+  // relobj_ we can derive the erratum_address and destination address.
+  void
+  update_erratum_address(AArch64_address output_section_addr)
+  {
+    const int BPI = AArch64_insn_utilities<big_endian>::BYTES_PER_INSN;
+    AArch64_address updated_addr = output_section_addr + this->sh_offset_;
+    this->set_erratum_address(updated_addr);
+    this->set_destination_address(updated_addr + BPI);
+  }
+
   // Comparator used to group Erratum_stubs in a set by (obj, shndx,
   // sh_offset). We do not include 'type' in the calculation, because there is
   // at most one stub type at (obj, shndx, sh_offset).
@@ -1049,6 +1061,17 @@ public:
     return this->sh_offset_ < k.sh_offset_;
   }
 
+  void
+  invalidate_erratum_stub()
+  {
+     gold_assert(this->erratum_insn_ != invalid_insn);
+     this->erratum_insn_ = invalid_insn;
+  }
+
+  bool
+  is_invalidated_erratum_stub()
+  { return this->erratum_insn_ == invalid_insn; }
+
 protected:
   virtual void
   do_write(unsigned char*, section_size_type);
@@ -1090,7 +1113,7 @@ public:
 
 private:
   // Section offset of "adrp". (We do not need a "adrp_shndx_" field, because we
-  // can can obtain it from its parent.)
+  // can obtain it from its parent.)
   const unsigned int adrp_sh_offset_;
 };
 
@@ -1346,7 +1369,8 @@ Reloc_stub<size, big_endian>::stub_type_for_reloc(
   return ST_LONG_BRANCH_ABS;
 }
 
-// A class to hold stubs for the ARM target.
+// A class to hold stubs for the ARM target. This contains 2 different types of
+// stubs - reloc stubs and erratum stubs.
 
 template<int size, bool big_endian>
 class Stub_table : public Output_data
@@ -1438,14 +1462,18 @@ class Stub_table : public Output_data
     return (p != this->reloc_stubs_.end()) ? p->second : NULL;
   }
 
-  // Relocate stubs in this stub table.
+  // Relocate reloc stubs in this stub table. This does not relocate erratum stubs.
   void
-  relocate_stubs(const The_relocate_info*,
-                The_target_aarch64*,
-                Output_section*,
-                unsigned char*,
-                AArch64_address,
-                section_size_type);
+  relocate_reloc_stubs(const The_relocate_info*,
+                       The_target_aarch64*,
+                       Output_section*,
+                       unsigned char*,
+                       AArch64_address,
+                       section_size_type);
+
+  // Relocate an erratum stub.
+  void
+  relocate_erratum_stub(The_erratum_stub*, unsigned char*);
 
   // Update data size at the end of a relaxation pass.  Return true if data size
   // is different from that of the previous relaxation pass.
@@ -1485,15 +1513,15 @@ class Stub_table : public Output_data
   { this->set_data_size(this->current_data_size()); }
 
  private:
-  // Relocate one stub.
+  // Relocate one reloc stub.
   void
-  relocate_stub(The_reloc_stub*,
-               const The_relocate_info*,
-               The_target_aarch64*,
-               Output_section*,
-               unsigned char*,
-               AArch64_address,
-               section_size_type);
+  relocate_reloc_stub(The_reloc_stub*,
+                      const The_relocate_info*,
+                      The_target_aarch64*,
+                      Output_section*,
+                      unsigned char*,
+                      AArch64_address,
+                      section_size_type);
 
  private:
   // Owner of this stub table.
@@ -1593,76 +1621,85 @@ Stub_table<size, big_endian>::add_reloc_stub(
 }
 
 
-// Relocate all stubs in this stub table.
+// Relocate an erratum stub.
 
 template<int size, bool big_endian>
 void
 Stub_table<size, big_endian>::
-relocate_stubs(const The_relocate_info* relinfo,
-              The_target_aarch64* target_aarch64,
-              Output_section* output_section,
-              unsigned char* view,
-              AArch64_address address,
-              section_size_type view_size)
+relocate_erratum_stub(The_erratum_stub* estub,
+                      unsigned char* view)
 {
-  // "view_size" is the total size of the stub_table.
-  gold_assert(address == this->address() &&
-             view_size == static_cast<section_size_type>(this->data_size()));
-  for(Reloc_stub_map_const_iter p = this->reloc_stubs_.begin();
-      p != this->reloc_stubs_.end(); ++p)
-    relocate_stub(p->second, relinfo, target_aarch64, output_section,
-                 view, address, view_size);
-
   // Just for convenience.
   const int BPI = AArch64_insn_utilities<big_endian>::BYTES_PER_INSN;
 
-  // Now 'relocate' erratum stubs.
-  for(Erratum_stub_set_iter i = this->erratum_stubs_.begin();
-      i != this->erratum_stubs_.end(); ++i)
+  gold_assert(!estub->is_invalidated_erratum_stub());
+  AArch64_address stub_address = this->erratum_stub_address(estub);
+  // The address of "b" in the stub that is to be "relocated".
+  AArch64_address stub_b_insn_address;
+  // Branch offset that is to be filled in "b" insn.
+  int b_offset = 0;
+  switch (estub->type())
     {
-      AArch64_address stub_address = this->erratum_stub_address(*i);
-      // The address of "b" in the stub that is to be "relocated".
-      AArch64_address stub_b_insn_address;
-      // Branch offset that is to be filled in "b" insn.
-      int b_offset = 0;
-      switch ((*i)->type())
-       {
-       case ST_E_843419:
-       case ST_E_835769:
-         // The 1st insn of the erratum could be a relocation spot,
-         // in this case we need to fix it with
-         // "(*i)->erratum_insn()".
-         elfcpp::Swap<32, big_endian>::writeval(
-             view + (stub_address - this->address()),
-             (*i)->erratum_insn());
-         // For the erratum, the 2nd insn is a b-insn to be patched
-         // (relocated).
-         stub_b_insn_address = stub_address + 1 * BPI;
-         b_offset = (*i)->destination_address() - stub_b_insn_address;
-         AArch64_relocate_functions<size, big_endian>::construct_b(
-             view + (stub_b_insn_address - this->address()),
-             ((unsigned int)(b_offset)) & 0xfffffff);
-         break;
-       default:
-         gold_unreachable();
-         break;
-       }
+    case ST_E_843419:
+    case ST_E_835769:
+      // The 1st insn of the erratum could be a relocation spot,
+      // in this case we need to fix it with
+      // "(*i)->erratum_insn()".
+      elfcpp::Swap<32, big_endian>::writeval(
+          view + (stub_address - this->address()),
+          estub->erratum_insn());
+      // For the erratum, the 2nd insn is a b-insn to be patched
+      // (relocated).
+      stub_b_insn_address = stub_address + 1 * BPI;
+      b_offset = estub->destination_address() - stub_b_insn_address;
+      AArch64_relocate_functions<size, big_endian>::construct_b(
+          view + (stub_b_insn_address - this->address()),
+          ((unsigned int)(b_offset)) & 0xfffffff);
+      break;
+    default:
+      gold_unreachable();
+      break;
     }
+  estub->invalidate_erratum_stub();
 }
 
 
-// Relocate one stub.  This is a helper for Stub_table::relocate_stubs().
+// Relocate only reloc stubs in this stub table. This does not relocate erratum
+// stubs.
 
 template<int size, bool big_endian>
 void
 Stub_table<size, big_endian>::
-relocate_stub(The_reloc_stub* stub,
-             const The_relocate_info* relinfo,
-             The_target_aarch64* target_aarch64,
-             Output_section* output_section,
-             unsigned char* view,
-             AArch64_address address,
-             section_size_type view_size)
+relocate_reloc_stubs(const The_relocate_info* relinfo,
+                     The_target_aarch64* target_aarch64,
+                     Output_section* output_section,
+                     unsigned char* view,
+                     AArch64_address address,
+                     section_size_type view_size)
+{
+  // "view_size" is the total size of the stub_table.
+  gold_assert(address == this->address() &&
+             view_size == static_cast<section_size_type>(this->data_size()));
+  for(Reloc_stub_map_const_iter p = this->reloc_stubs_.begin();
+      p != this->reloc_stubs_.end(); ++p)
+    relocate_reloc_stub(p->second, relinfo, target_aarch64, output_section,
+                        view, address, view_size);
+}
+
+
+// Relocate one reloc stub. This is a helper for
+// Stub_table::relocate_reloc_stubs().
+
+template<int size, bool big_endian>
+void
+Stub_table<size, big_endian>::
+relocate_reloc_stub(The_reloc_stub* stub,
+                    const The_relocate_info* relinfo,
+                    The_target_aarch64* target_aarch64,
+                    Output_section* output_section,
+                    unsigned char* view,
+                    AArch64_address address,
+                    section_size_type view_size)
 {
   // "offset" is the offset from the beginning of the stub_table.
   section_size_type offset = stub->offset();
@@ -1670,8 +1707,8 @@ relocate_stub(The_reloc_stub* stub,
   // "view_size" is the total size of the stub_table.
   gold_assert(offset + stub_size <= view_size);
 
-  target_aarch64->relocate_stub(stub, relinfo, output_section,
-                               view + offset, address + offset, view_size);
+  target_aarch64->relocate_reloc_stub(stub, relinfo, output_section,
+                                      view + offset, address + offset, view_size);
 }
 
 
@@ -1829,15 +1866,17 @@ class AArch64_relobj : public Sized_relobj_file<size, big_endian>
                         Stringpool_template<char>*);
 
  private:
-  // Fix all errata in the object.
+  // Fix all errata in the object, and for each erratum, relocate corresponding
+  // erratum stub.
   void
-  fix_errata(typename Sized_relobj_file<size, big_endian>::Views* pviews);
+  fix_errata_and_relocate_erratum_stubs(
+      typename Sized_relobj_file<size, big_endian>::Views* pviews);
 
   // Try to fix erratum 843419 in an optimized way. Return true if patch is
   // applied.
   bool
   try_fix_erratum_843419_optimized(
-      The_erratum_stub*,
+      The_erratum_stub*, AArch64_address,
       typename Sized_relobj_file<size, big_endian>::View_size&);
 
   // Whether a section needs to be scanned for relocation stubs.
@@ -1943,15 +1982,17 @@ AArch64_relobj<size, big_endian>::do_count_local_symbols(
 }
 
 
-// Fix all errata in the object.
+// Fix all errata in the object and for each erratum, we relocate the
+// corresponding erratum stub (by calling Stub_table::relocate_erratum_stub).
 
 template<int size, bool big_endian>
 void
-AArch64_relobj<size, big_endian>::fix_errata(
+AArch64_relobj<size, big_endian>::fix_errata_and_relocate_erratum_stubs(
     typename Sized_relobj_file<size, big_endian>::Views* pviews)
 {
   typedef typename elfcpp::Swap<32,big_endian>::Valtype Insntype;
   unsigned int shnum = this->shnum();
+  const Relobj::Output_sections& out_sections(this->output_sections());
   for (unsigned int i = 1; i < shnum; ++i)
     {
       The_stub_table* stub_table = this->stub_table(i);
@@ -1960,34 +2001,61 @@ AArch64_relobj<size, big_endian>::fix_errata(
       std::pair<Erratum_stub_set_iter, Erratum_stub_set_iter>
        ipair(stub_table->find_erratum_stubs_for_input_section(this, i));
       Erratum_stub_set_iter p = ipair.first, end = ipair.second;
+      typename Sized_relobj_file<size, big_endian>::View_size&
+       pview((*pviews)[i]);
+      AArch64_address view_offset = 0;
+      if (pview.is_input_output_view)
+       {
+         // In this case, write_sections has not added the output offset to
+         // the view's address, so we must do so. Currently this only happens
+         // for a relaxed section.
+         unsigned int index = this->adjust_shndx(i);
+         const Output_relaxed_input_section* poris =
+             out_sections[index]->find_relaxed_input_section(this, index);
+         gold_assert(poris != NULL);
+         view_offset = poris->address() - pview.address;
+       }
+
       while (p != end)
        {
          The_erratum_stub* stub = *p;
-         typename Sized_relobj_file<size, big_endian>::View_size&
-           pview((*pviews)[i]);
 
          // Double check data before fix.
-         gold_assert(pview.address + stub->sh_offset()
+         gold_assert(pview.address + view_offset + stub->sh_offset()
                      == stub->erratum_address());
 
          // Update previously recorded erratum insn with relocated
          // version.
          Insntype* ip =
-           reinterpret_cast<Insntype*>(pview.view + stub->sh_offset());
+           reinterpret_cast<Insntype*>(
+             pview.view + view_offset + stub->sh_offset());
          Insntype insn_to_fix = ip[0];
          stub->update_erratum_insn(insn_to_fix);
 
          // First try to see if erratum is 843419 and if it can be fixed
          // without using branch-to-stub.
-         if (!try_fix_erratum_843419_optimized(stub, pview))
+         if (!try_fix_erratum_843419_optimized(stub, view_offset, pview))
            {
              // Replace the erratum insn with a branch-to-stub.
              AArch64_address stub_address =
                stub_table->erratum_stub_address(stub);
              unsigned int b_offset = stub_address - stub->erratum_address();
              AArch64_relocate_functions<size, big_endian>::construct_b(
-               pview.view + stub->sh_offset(), b_offset & 0xfffffff);
+               pview.view + view_offset + stub->sh_offset(),
+               b_offset & 0xfffffff);
            }
+
+          // Erratum fix is done (or skipped), continue to relocate erratum
+          // stub. Note, when erratum fix is skipped (either because we
+          // proactively change the code sequence or the code sequence is
+          // changed by relaxation, etc), we can still safely relocate the
+          // erratum stub, ignoring the fact the erratum could never be
+          // executed.
+          stub_table->relocate_erratum_stub(
+           stub,
+           pview.view + (stub_table->address() - pview.address));
+
+          // Next erratum stub.
          ++p;
        }
     }
@@ -2003,7 +2071,7 @@ AArch64_relobj<size, big_endian>::fix_errata(
 template<int size, bool big_endian>
 bool
 AArch64_relobj<size, big_endian>::try_fix_erratum_843419_optimized(
-    The_erratum_stub* stub,
+    The_erratum_stub* stub, AArch64_address view_offset,
     typename Sized_relobj_file<size, big_endian>::View_size& pview)
 {
   if (stub->type() != ST_E_843419)
@@ -2013,9 +2081,11 @@ AArch64_relobj<size, big_endian>::try_fix_erratum_843419_optimized(
   typedef typename elfcpp::Swap<32,big_endian>::Valtype Insntype;
   E843419_stub<size, big_endian>* e843419_stub =
     reinterpret_cast<E843419_stub<size, big_endian>*>(stub);
-  AArch64_address pc = pview.address + e843419_stub->adrp_sh_offset();
+  AArch64_address pc =
+    pview.address + view_offset + e843419_stub->adrp_sh_offset();
   unsigned int adrp_offset = e843419_stub->adrp_sh_offset ();
-  Insntype* adrp_view = reinterpret_cast<Insntype*>(pview.view + adrp_offset);
+  Insntype* adrp_view =
+    reinterpret_cast<Insntype*>(pview.view + view_offset + adrp_offset);
   Insntype adrp_insn = adrp_view[0];
 
   // If the instruction at adrp_sh_offset is "mrs R, tpidr_el0", it may come
@@ -2031,8 +2101,9 @@ AArch64_relobj<size, big_endian>::try_fix_erratum_843419_optimized(
   // return true.
   if (!Insn_utilities::is_adrp(adrp_insn) && adrp_offset)
     {
-      Insntype* prev_view
-       = reinterpret_cast<Insntype*>(pview.view + adrp_offset - 4);
+      Insntype* prev_view =
+       reinterpret_cast<Insntype*>(
+         pview.view + view_offset + adrp_offset - 4);
       Insntype prev_insn = prev_view[0];
 
       if (Insn_utilities::is_mrs_tpidr_el0(prev_insn))
@@ -2086,16 +2157,19 @@ AArch64_relobj<size, big_endian>::do_relocate_sections(
   if (parameters->options().relocatable())
     return;
 
+  // This part only relocates erratum stubs that belong to input sections of this
+  // object file.
   if (parameters->options().fix_cortex_a53_843419()
       || parameters->options().fix_cortex_a53_835769())
-    this->fix_errata(pviews);
+    this->fix_errata_and_relocate_erratum_stubs(pviews);
 
   Relocate_info<size, big_endian> relinfo;
   relinfo.symtab = symtab;
   relinfo.layout = layout;
   relinfo.object = this;
 
-  // Relocate stub tables.
+  // This part relocates all reloc stubs that are contained in stub_tables of
+  // this object file.
   unsigned int shnum = this->shnum();
   The_target_aarch64* target = The_target_aarch64::current_target();
 
@@ -2124,8 +2198,8 @@ AArch64_relobj<size, big_endian>::do_relocate_sections(
          unsigned char* view = view_struct.view + offset;
          AArch64_address address = stub_table->address();
          section_size_type view_size = stub_table->data_size();
-         stub_table->relocate_stubs(&relinfo, target, os, view, address,
-                                    view_size);
+         stub_table->relocate_reloc_stubs(&relinfo, target, os, view, address,
+                                          view_size);
        }
     }
 }
@@ -2242,6 +2316,19 @@ AArch64_relobj<size, big_endian>::scan_errata(
       output_address = poris->address();
     }
 
+  // Update the addresses in previously generated erratum stubs. Unlike when
+  // we scan relocations for stubs, if section addresses have changed due to
+  // other relaxations we are unlikely to scan the same erratum instances
+  // again.
+  The_stub_table* stub_table = this->stub_table(shndx);
+  if (stub_table)
+    {
+      std::pair<Erratum_stub_set_iter, Erratum_stub_set_iter>
+         ipair(stub_table->find_erratum_stubs_for_input_section(this, shndx));
+      for (Erratum_stub_set_iter p = ipair.first;  p != ipair.second; ++p)
+          (*p)->update_erratum_address(output_address);
+    }
+
   section_size_type input_view_size = 0;
   const unsigned char* input_view =
     this->section_contents(shndx, &input_view_size, false);
@@ -3016,11 +3103,11 @@ class Target_aarch64 : public Sized_target<size, big_endian>
       Address view_address,
       section_size_type);
 
-  // Relocate a single stub.
+  // Relocate a single reloc stub.
   void
-  relocate_stub(The_reloc_stub*, const Relocate_info<size, big_endian>*,
-               Output_section*, unsigned char*, Address,
-               section_size_type);
+  relocate_reloc_stub(The_reloc_stub*, const Relocate_info<size, big_endian>*,
+                      Output_section*, unsigned char*, Address,
+                      section_size_type);
 
   // Get the default AArch64 target.
   static This*
@@ -3461,7 +3548,7 @@ const Target::Target_info Target_aarch64<64, false>::aarch64_info =
   false,               // has_make_symbol
   false,               // has_resolve
   false,               // has_code_fill
-  true,                        // is_default_stack_executable
+  false,               // is_default_stack_executable
   true,                        // can_icf_inline_merge_sections
   '\0',                        // wrap_char
   "/lib/ld.so.1",      // program interpreter
@@ -3478,6 +3565,7 @@ const Target::Target_info Target_aarch64<64, false>::aarch64_info =
   NULL,                        // attributes_vendor
   "_start",            // entry_symbol_name
   32,                  // hash_entry_size
+  elfcpp::SHT_PROGBITS,        // unwind_section_type
 };
 
 template<>
@@ -3489,7 +3577,7 @@ const Target::Target_info Target_aarch64<32, false>::aarch64_info =
   false,               // has_make_symbol
   false,               // has_resolve
   false,               // has_code_fill
-  true,                        // is_default_stack_executable
+  false,               // is_default_stack_executable
   false,               // can_icf_inline_merge_sections
   '\0',                        // wrap_char
   "/lib/ld.so.1",      // program interpreter
@@ -3506,6 +3594,7 @@ const Target::Target_info Target_aarch64<32, false>::aarch64_info =
   NULL,                        // attributes_vendor
   "_start",            // entry_symbol_name
   32,                  // hash_entry_size
+  elfcpp::SHT_PROGBITS,        // unwind_section_type
 };
 
 template<>
@@ -3517,7 +3606,7 @@ const Target::Target_info Target_aarch64<64, true>::aarch64_info =
   false,               // has_make_symbol
   false,               // has_resolve
   false,               // has_code_fill
-  true,                        // is_default_stack_executable
+  false,               // is_default_stack_executable
   true,                        // can_icf_inline_merge_sections
   '\0',                        // wrap_char
   "/lib/ld.so.1",      // program interpreter
@@ -3534,6 +3623,7 @@ const Target::Target_info Target_aarch64<64, true>::aarch64_info =
   NULL,                        // attributes_vendor
   "_start",            // entry_symbol_name
   32,                  // hash_entry_size
+  elfcpp::SHT_PROGBITS,        // unwind_section_type
 };
 
 template<>
@@ -3545,7 +3635,7 @@ const Target::Target_info Target_aarch64<32, true>::aarch64_info =
   false,               // has_make_symbol
   false,               // has_resolve
   false,               // has_code_fill
-  true,                        // is_default_stack_executable
+  false,               // is_default_stack_executable
   false,               // can_icf_inline_merge_sections
   '\0',                        // wrap_char
   "/lib/ld.so.1",      // program interpreter
@@ -3562,6 +3652,7 @@ const Target::Target_info Target_aarch64<32, true>::aarch64_info =
   NULL,                        // attributes_vendor
   "_start",            // entry_symbol_name
   32,                  // hash_entry_size
+  elfcpp::SHT_PROGBITS,        // unwind_section_type
 };
 
 // Get the GOT section, creating it if necessary.
@@ -3768,13 +3859,13 @@ Target_aarch64<size, big_endian>::scan_reloc_for_stub(
       if (gsym->use_plt_offset(arp->reference_flags()))
        {
          // This uses a PLT, change the symbol value.
-         symval.set_output_value(this->plt_section()->address()
-                                 + gsym->plt_offset());
+         symval.set_output_value(this->plt_address_for_global(gsym));
          psymval = &symval;
        }
       else if (gsym->is_undefined())
        {
-         // There is no need to generate a stub symbol is undefined.
+          // There is no need to generate a stub symbol if the original symbol
+          // is undefined.
           gold_debug(DEBUG_TARGET,
                      "stub: not creating a stub for undefined symbol %s in file %s",
                      gsym->name(), aarch64_relobj->name().c_str());
@@ -3881,6 +3972,7 @@ Target_aarch64<size, big_endian>::scan_reloc_section_for_stubs(
       const Symbol_value<size> *psymval;
       bool is_defined_in_discarded_section;
       unsigned int shndx;
+      const Symbol* gsym = NULL;
       if (r_sym < local_count)
        {
          sym = NULL;
@@ -3933,7 +4025,6 @@ Target_aarch64<size, big_endian>::scan_reloc_section_for_stubs(
        }
       else
        {
-         const Symbol* gsym;
          gsym = object->global_symbol(r_sym);
          gold_assert(gsym != NULL);
          if (gsym->is_forwarder())
@@ -3974,16 +4065,16 @@ Target_aarch64<size, big_endian>::scan_reloc_section_for_stubs(
       Symbol_value<size> symval2;
       if (is_defined_in_discarded_section)
        {
+         std::string name = object->section_name(relinfo->data_shndx);
+
          if (comdat_behavior == CB_UNDETERMINED)
-           {
-             std::string name = object->section_name(relinfo->data_shndx);
              comdat_behavior = default_comdat_behavior.get(name.c_str());
-           }
+
          if (comdat_behavior == CB_PRETEND)
            {
              bool found;
              typename elfcpp::Elf_types<size>::Elf_Addr value =
-               object->map_to_kept_section(shndx, &found);
+               object->map_to_kept_section(shndx, name, &found);
              if (found)
                symval2.set_output_value(value + psymval->input_value());
              else
@@ -3991,21 +4082,14 @@ Target_aarch64<size, big_endian>::scan_reloc_section_for_stubs(
            }
          else
            {
-             if (comdat_behavior == CB_WARNING)
-               gold_warning_at_location(relinfo, i, offset,
-                                        _("relocation refers to discarded "
-                                          "section"));
+             if (comdat_behavior == CB_ERROR)
+               issue_discarded_error(relinfo, i, offset, r_sym, gsym);
              symval2.set_output_value(0);
            }
          symval2.set_no_output_symtab_entry();
          psymval = &symval2;
        }
 
-      // If symbol is a section symbol, we don't know the actual type of
-      // destination.  Give up.
-      if (psymval->is_section_symbol())
-       continue;
-
       this->scan_reloc_for_stub(relinfo, r_type, sym, r_sym, psymval,
                                addend, view_address + offset);
     }  // End of iterating relocs in a section
@@ -4040,16 +4124,16 @@ Target_aarch64<size, big_endian>::scan_section_for_stubs(
 }
 
 
-// Relocate a single stub.
+// Relocate a single reloc stub.
 
 template<int size, bool big_endian>
 void Target_aarch64<size, big_endian>::
-relocate_stub(The_reloc_stub* stub,
-             const The_relocate_info*,
-             Output_section*,
-             unsigned char* view,
-             Address address,
-             section_size_type)
+relocate_reloc_stub(The_reloc_stub* stub,
+                    const The_relocate_info*,
+                    Output_section*,
+                    unsigned char* view,
+                    Address address,
+                    section_size_type)
 {
   typedef AArch64_relocate_functions<size, big_endian> The_reloc_functions;
   typedef typename The_reloc_functions::Status The_reloc_functions_status;
@@ -5437,8 +5521,14 @@ maybe_apply_stub(unsigned int r_type,
 
   const The_aarch64_relobj* aarch64_relobj =
       static_cast<const The_aarch64_relobj*>(object);
-  // We don't create stubs for undefined symbols so don't look for one.
-  if (gsym && gsym->is_undefined())
+  const AArch64_reloc_property* arp =
+    aarch64_reloc_property_table->get_reloc_property(r_type);
+  gold_assert(arp != NULL);
+
+  // We don't create stubs for undefined symbols, but do for weak.
+  if (gsym
+      && !gsym->use_plt_offset(arp->reference_flags())
+      && gsym->is_undefined())
     {
       gold_debug(DEBUG_TARGET,
                 "stub: looking for a stub for undefined symbol %s in file %s",
@@ -5457,9 +5547,6 @@ maybe_apply_stub(unsigned int r_type,
   Address new_branch_target = stub_table->address() + stub->offset();
   typename elfcpp::Swap<size, big_endian>::Valtype branch_offset =
       new_branch_target - address;
-  const AArch64_reloc_property* arp =
-      aarch64_reloc_property_table->get_reloc_property(r_type);
-  gold_assert(arp != NULL);
   typename This::Status status = This::template
       rela_general<32>(view, branch_offset, 0, arp);
   if (status != This::STATUS_OKAY)
@@ -5831,6 +5918,14 @@ Target_aarch64<size, big_endian>::optimize_tls_reloc(bool is_final,
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12:
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
       // When we already have Local-Exec, there is nothing further we
       // can do.
       return tls::TLSOPT_NONE;
@@ -6177,6 +6272,14 @@ Target_aarch64<size, big_endian>::Scan::local(
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12:
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
       {
        layout->set_has_static_tls();
        bool output_is_shared = parameters->options().shared();
@@ -6393,6 +6496,17 @@ Target_aarch64<size, big_endian>::Scan::global(
          gold_error(_("%s: unsupported reloc %u in pos independent link."),
                     object->name().c_str(), r_type);
        }
+      // Make a PLT entry if necessary.
+      if (gsym->needs_plt_entry())
+       {
+         target->make_plt_entry(symtab, layout, gsym);
+         // 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();
+       }
       break;
 
     case elfcpp::R_AARCH64_LD_PREL_LO19:        // 273
@@ -6594,7 +6708,15 @@ Target_aarch64<size, big_endian>::Scan::global(
     case elfcpp::R_AARCH64_TLSLE_MOVW_TPREL_G0_NC:
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12:
-    case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:  // Local executable
+    case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:  // Local executable
       layout->set_has_static_tls();
       if (parameters->options().shared())
        gold_error(_("%s: unsupported TLSLE reloc type %u in shared objects."),
@@ -6854,11 +6976,11 @@ Target_aarch64<size, big_endian>::do_finalize_sections(
     }
 
   // Set the size of the _GLOBAL_OFFSET_TABLE_ symbol to the size of
-  // the .got.plt section.
+  // the .got section.
   Symbol* sym = this->global_offset_table_;
   if (sym != NULL)
     {
-      uint64_t data_size = this->got_plt_->current_data_size();
+      uint64_t data_size = this->got_->current_data_size();
       symtab->get_sized_symbol<size>(sym)->set_symsize(data_size);
 
       // If the .got section is more than 0x8000 bytes, we add
@@ -7191,6 +7313,14 @@ Target_aarch64<size, big_endian>::Relocate::relocate(
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12:
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
     case elfcpp::R_AARCH64_TLSDESC_ADR_PAGE21:
     case elfcpp::R_AARCH64_TLSDESC_LD64_LO12:
     case elfcpp::R_AARCH64_TLSDESC_ADD_LO12:
@@ -7470,6 +7600,14 @@ Target_aarch64<size, big_endian>::Relocate::relocate_tls(
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_HI12:
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12:
     case elfcpp::R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+    case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12:
+    case elfcpp::R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
       {
        gold_assert(tls_segment != NULL);
        AArch64_address value = psymval->value(object, 0);
@@ -7523,15 +7661,15 @@ Target_aarch64<size, big_endian>::Relocate::relocate_tls(
            tls_got_offset_type = (tlsopt == tls::TLSOPT_TO_IE
                                   ? GOT_TYPE_TLS_OFFSET
                                   : GOT_TYPE_TLS_DESC);
-           unsigned int got_tlsdesc_offset = 0;
+           int got_tlsdesc_offset = 0;
            if (r_type != elfcpp::R_AARCH64_TLSDESC_CALL
                && tlsopt == tls::TLSOPT_NONE)
              {
                // We created GOT entries in the .got.tlsdesc portion of the
                // .got.plt section, but the offset stored in the symbol is the
                // offset within .got.tlsdesc.
-               got_tlsdesc_offset = (target->got_->data_size()
-                                     + target->got_plt_section()->data_size());
+               got_tlsdesc_offset = (target->got_tlsdesc_->address()
+                                     - target->got_->address());
              }
            typename elfcpp::Elf_types<size>::Elf_Addr got_entry_address;
            if (gsym != NULL)
@@ -7736,8 +7874,8 @@ Target_aarch64<size, big_endian>::Relocate::tls_ld_to_le(
     {
       // Ideally we should give up gd_to_le relaxation and do gd access.
       // However the gd_to_le relaxation decision has been made early
-      // in the scan stage, where we did not allocate any GOT entry for
-      // this symbol. Therefore we have to exit and report error now.
+      // in the scan stage, where we did not allocate a GOT entry for
+      // this symbol. Therefore we have to exit and report an error now.
       gold_error(_("unexpected reloc insn sequence while relaxing "
                   "tls gd to le for reloc %u."), r_type);
       return aarch64_reloc_funcs::STATUS_BAD_RELOC;