Remove unnecessary target dependencies on relocation format.
[deliverable/binutils-gdb.git] / gold / aarch64.cc
index 8dfd933966ba869fa0da22ec59a3d27647f75fe4..5ca5e0accfff921463a4714224802ba0099d6d71 100644 (file)
@@ -77,7 +77,10 @@ class AArch64_insn_utilities
 public:
   typedef typename elfcpp::Swap<32, big_endian>::Valtype Insntype;
 
-  static const int BYTES_PER_INSN = 4;
+  static const int BYTES_PER_INSN;
+
+  // Zero register encoding - 31.
+  static const unsigned int AARCH64_ZR;
 
   static unsigned int
   aarch64_bit(Insntype insn, int pos)
@@ -87,6 +90,22 @@ public:
   aarch64_bits(Insntype insn, int pos, int l)
   { return (insn >> pos) & ((1 << l) - 1); }
 
+  // Get the encoding field "op31" of 3-source data processing insns. "op31" is
+  // the name defined in armv8 insn manual C3.5.9.
+  static unsigned int
+  aarch64_op31(Insntype insn)
+  { return aarch64_bits(insn, 21, 3); }
+
+  // Get the encoding field "ra" of 3-source data processing insns. "ra" is the
+  // third source register. See armv8 insn manual C3.5.9.
+  static unsigned int
+  aarch64_ra(Insntype insn)
+  { return aarch64_bits(insn, 10, 5); }
+
+  static bool
+  is_adr(const Insntype insn)
+  { return (insn & 0x9F000000) == 0x10000000; }
+
   static bool
   is_adrp(const Insntype insn)
   { return (insn & 0x9F000000) == 0x90000000; }
@@ -111,6 +130,39 @@ public:
   aarch64_rt2(const Insntype insn)
   { return aarch64_bits(insn, 10, 5); }
 
+  // Encode imm21 into adr. Signed imm21 is in the range of [-1M, 1M).
+  static Insntype
+  aarch64_adr_encode_imm(Insntype adr, int imm21)
+  {
+    gold_assert(is_adr(adr));
+    gold_assert(-(1 << 20) <= imm21 && imm21 < (1 << 20));
+    const int mask19 = (1 << 19) - 1;
+    const int mask2 = 3;
+    adr &= ~((mask19 << 5) | (mask2 << 29));
+    adr |= ((imm21 & mask2) << 29) | (((imm21 >> 2) & mask19) << 5);
+    return adr;
+  }
+
+  // Retrieve encoded adrp 33-bit signed imm value. This value is obtained by
+  // 21-bit signed imm encoded in the insn multiplied by 4k (page size) and
+  // 64-bit sign-extended, resulting in [-4G, 4G) with 12-lsb being 0.
+  static int64_t
+  aarch64_adrp_decode_imm(const Insntype adrp)
+  {
+    const int mask19 = (1 << 19) - 1;
+    const int mask2 = 3;
+    gold_assert(is_adrp(adrp));
+    // 21-bit imm encoded in adrp.
+    uint64_t imm = ((adrp >> 29) & mask2) | (((adrp >> 5) & mask19) << 2);
+    // Retrieve msb of 21-bit-signed imm for sign extension.
+    uint64_t msbt = (imm >> 20) & 1;
+    // Real value is imm multipled by 4k. Value now has 33-bit information.
+    int64_t value = imm << 12;
+    // Sign extend to 64-bit by repeating msbt 31 (64-33) times and merge it
+    // with value.
+    return ((((uint64_t)(1) << 32) - msbt) << 33) | value;
+  }
+
   static bool
   aarch64_b(const Insntype insn)
   { return (insn & 0xFC000000) == 0x14000000; }
@@ -330,8 +382,42 @@ public:
        return true;
       }
     return false;
+  }  // End of "aarch64_mem_op_p".
+
+  // Return true if INSN is mac insn.
+  static bool
+  aarch64_mac(Insntype insn)
+  { return (insn & 0xff000000) == 0x9b000000; }
+
+  // Return true if INSN is multiply-accumulate.
+  // (This is similar to implementaton in elfnn-aarch64.c.)
+  static bool
+  aarch64_mlxl(Insntype insn)
+  {
+    uint32_t op31 = aarch64_op31(insn);
+    if (aarch64_mac(insn)
+       && (op31 == 0 || op31 == 1 || op31 == 5)
+       /* Exclude MUL instructions which are encoded as a multiple-accumulate
+          with RA = XZR.  */
+       && aarch64_ra(insn) != AARCH64_ZR)
+      {
+       return true;
+      }
+    return false;
   }
-};
+};  // End of "AArch64_insn_utilities".
+
+
+// Insn length in byte.
+
+template<bool big_endian>
+const int AArch64_insn_utilities<big_endian>::BYTES_PER_INSN = 4;
+
+
+// Zero register encoding - 31.
+
+template<bool big_endian>
+const unsigned int AArch64_insn_utilities<big_endian>::AARCH64_ZR = 0x1f;
 
 
 // Output_data_got_aarch64 class.
@@ -603,8 +689,11 @@ enum
   // Stub for erratum 843419 handling.
   ST_E_843419 = 4,
 
+  // Stub for erratum 835769 handling.
+  ST_E_835769 = 5,
+
   // Number of total stub types.
-  ST_NUMBER = 5
+  ST_NUMBER = 6
 };
 
 
@@ -695,6 +784,9 @@ Stub_template_repertoire<big_endian>::Stub_template_repertoire()
       0x14000000,    /* b <label> */
     };
 
+  // ST_E_835769 has the same stub template as ST_E_843419.
+  const static Insntype* ST_E_835769_INSNS = ST_E_843419_INSNS;
+
 #define install_insn_template(T) \
   const static Stub_template<big_endian> template_##T = {  \
     T##_INSNS, sizeof(T##_INSNS) / sizeof(T##_INSNS[0]) }; \
@@ -705,6 +797,7 @@ Stub_template_repertoire<big_endian>::Stub_template_repertoire()
   install_insn_template(ST_LONG_BRANCH_ABS);
   install_insn_template(ST_LONG_BRANCH_PCREL);
   install_insn_template(ST_E_843419);
+  install_insn_template(ST_E_835769);
 
 #undef install_insn_template
 }
@@ -838,6 +931,7 @@ class Erratum_stub : public Stub_base<size, big_endian>
 public:
   typedef AArch64_relobj<size, big_endian> The_aarch64_relobj;
   typedef typename elfcpp::Elf_types<size>::Elf_Addr AArch64_address;
+  typedef AArch64_insn_utilities<big_endian> Insn_utilities;
   typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype;
 
   static const int STUB_ADDR_ALIGN;
@@ -882,6 +976,38 @@ public:
   set_erratum_insn(Insntype insn)
   { this->erratum_insn_ = insn; }
 
+  // For 843419, the erratum insn is ld/st xt, [xn, #uimm], which may be a
+  // relocation spot, in this case, the erratum_insn_ recorded at scanning phase
+  // is no longer the one we want to write out to the stub, update erratum_insn_
+  // with relocated version. Also note that in this case xn must not be "PC", so
+  // it is safe to move the erratum insn from the origin place to the stub. For
+  // 835769, the erratum insn is multiply-accumulate insn, which could not be a
+  // relocation spot (assertion added though).
+  void
+  update_erratum_insn(Insntype insn)
+  {
+    gold_assert(this->erratum_insn_ != this->invalid_insn);
+    switch (this->type())
+      {
+      case ST_E_843419:
+       gold_assert(Insn_utilities::aarch64_ldst_uimm(insn));
+       gold_assert(Insn_utilities::aarch64_ldst_uimm(this->erratum_insn()));
+       gold_assert(Insn_utilities::aarch64_rd(insn) ==
+                   Insn_utilities::aarch64_rd(this->erratum_insn()));
+       gold_assert(Insn_utilities::aarch64_rn(insn) ==
+                   Insn_utilities::aarch64_rn(this->erratum_insn()));
+       // Update plain ld/st insn with relocated insn.
+       this->erratum_insn_ = insn;
+       break;
+      case ST_E_835769:
+       gold_assert(insn == this->erratum_insn());
+       break;
+      default:
+       gold_unreachable();
+      }
+  }
+
+
   // Return the address where an erratum must be done.
   AArch64_address
   erratum_address() const
@@ -930,6 +1056,35 @@ private:
   AArch64_address erratum_address_;
 };  // End of "Erratum_stub".
 
+
+// Erratum sub class to wrap additional info needed by 843419.  In fixing this
+// erratum, we may choose to replace 'adrp' with 'adr', in this case, we need
+// adrp's code position (two or three insns before erratum insn itself).
+
+template<int size, bool big_endian>
+class E843419_stub : public Erratum_stub<size, big_endian>
+{
+public:
+  typedef typename AArch64_insn_utilities<big_endian>::Insntype Insntype;
+
+  E843419_stub(AArch64_relobj<size, big_endian>* relobj,
+                     unsigned int shndx, unsigned int sh_offset,
+                     unsigned int adrp_sh_offset)
+    : Erratum_stub<size, big_endian>(relobj, ST_E_843419, shndx, sh_offset),
+      adrp_sh_offset_(adrp_sh_offset)
+  {}
+
+  unsigned int
+  adrp_sh_offset() const
+  { return this->adrp_sh_offset_; }
+
+private:
+  // Section offset of "adrp". (We do not need a "adrp_shndx_" field, because we
+  // can can obtain it from its parent.)
+  const unsigned int adrp_sh_offset_;
+};
+
+
 template<int size, bool big_endian>
 const int Erratum_stub<size, big_endian>::STUB_ADDR_ALIGN = 4;
 
@@ -953,9 +1108,9 @@ Erratum_stub<size, big_endian>::do_write(unsigned char* view, section_size_type)
   const Insntype* insns = this->insns();
   uint32_t num_insns = this->insn_num();
   Insntype* ip = reinterpret_cast<Insntype*>(view);
-  // For current implemnted erratum 843419, (and 835769 which is to be
-  // implemented soon), the first insn in the stub is always a copy of the
-  // problematic insn (in 843419, the mem access insn), followed by a jump-back.
+  // For current implemented erratum 843419 and 835769, the first insn in the
+  // stub is always a copy of the problematic insn (in 843419, the mem access
+  // insn, in 835769, the mac insn), followed by a jump-back.
   elfcpp::Swap<32, big_endian>::writeval(ip, this->erratum_insn());
   for (uint32_t i = 1; i < num_insns; ++i)
     elfcpp::Swap<32, big_endian>::writeval(ip + i, insns[i]);
@@ -1361,7 +1516,7 @@ Stub_table<size, big_endian>::add_erratum_stub(The_erratum_stub* stub)
 }
 
 
-// Find if such erratum exists for givein (obj, shndx, sh_offset).
+// Find if such erratum exists for given (obj, shndx, sh_offset).
 
 template<int size, bool big_endian>
 Erratum_stub<size, big_endian>*
@@ -1461,6 +1616,13 @@ relocate_stubs(const The_relocate_info* relinfo,
       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;
@@ -1580,12 +1742,12 @@ class AArch64_relobj : public Sized_relobj_file<size, big_endian>
     this->stub_tables_[shndx] = stub_table;
   }
 
-  // Entrance to erratum_843419 scanning.
+  // Entrance to errata scanning.
   void
-  scan_erratum_843419(unsigned int shndx,
-                     const elfcpp::Shdr<size, big_endian>&,
-                     Output_section*, const Symbol_table*,
-                     The_target_aarch64*);
+  scan_errata(unsigned int shndx,
+             const elfcpp::Shdr<size, big_endian>&,
+             Output_section*, const Symbol_table*,
+             The_target_aarch64*);
 
   // Scan all relocation sections for stub generation.
   void
@@ -1658,6 +1820,13 @@ class AArch64_relobj : public Sized_relobj_file<size, big_endian>
   void
   fix_errata(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*,
+      typename Sized_relobj_file<size, big_endian>::View_size&);
+
   // Whether a section needs to be scanned for relocation stubs.
   bool
   section_needs_reloc_stub_scanning(const elfcpp::Shdr<size, big_endian>&,
@@ -1682,7 +1851,8 @@ AArch64_relobj<size, big_endian>::do_count_local_symbols(
 
   // Only erratum-fixing work needs mapping symbols, so skip this time consuming
   // processing if not fixing erratum.
-  if (!parameters->options().fix_cortex_a53_843419())
+  if (!parameters->options().fix_cortex_a53_843419()
+      && !parameters->options().fix_cortex_a53_835769())
     return;
 
   const unsigned int loccount = this->local_symbol_count();
@@ -1734,10 +1904,17 @@ AArch64_relobj<size, big_endian>::do_count_local_symbols(
       Symbol_value<size>& lv((*plocal_values)[i]);
       AArch64_address input_value = lv.input_value();
 
-      // Check to see if this is a mapping symbol.
+      // Check to see if this is a mapping symbol. AArch64 mapping symbols are
+      // defined in "ELF for the ARM 64-bit Architecture", Table 4-4, Mapping
+      // symbols.
+      // Mapping symbols could be one of the following 4 forms -
+      //   a) $x
+      //   b) $x.<any...>
+      //   c) $d
+      //   d) $d.<any...>
       const char* sym_name = pnames + sym.get_st_name();
       if (sym_name[0] == '$' && (sym_name[1] == 'x' || sym_name[1] == 'd')
-         && sym_name[2] == '\0')
+         && (sym_name[2] == '\0' || sym_name[2] == '.'))
        {
          bool is_ordinary;
          unsigned int input_shndx =
@@ -1777,24 +1954,85 @@ AArch64_relobj<size, big_endian>::fix_errata(
            pview((*pviews)[i]);
 
          // Double check data before fix.
+         gold_assert(pview.address + stub->sh_offset()
+                     == stub->erratum_address());
+
+         // Update previously recorded erratum insn with relocated
+         // version.
          Insntype* ip =
            reinterpret_cast<Insntype*>(pview.view + stub->sh_offset());
          Insntype insn_to_fix = ip[0];
-         gold_assert(insn_to_fix == stub->erratum_insn());
-         gold_assert(pview.address + stub->sh_offset()
-                     == stub->erratum_address());
+         stub->update_erratum_insn(insn_to_fix);
 
-         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);
+         // 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))
+           {
+             // 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);
+           }
          ++p;
        }
     }
 }
 
 
+// This is an optimization for 843419. This erratum requires the sequence begin
+// with 'adrp', when final value calculated by adrp fits in adr, we can just
+// replace 'adrp' with 'adr', so we save 2 jumps per occurrence. (Note, however,
+// in this case, we do not delete the erratum stub (too late to do so), it is
+// merely generated without ever being called.)
+
+template<int size, bool big_endian>
+bool
+AArch64_relobj<size, big_endian>::try_fix_erratum_843419_optimized(
+    The_erratum_stub* stub,
+    typename Sized_relobj_file<size, big_endian>::View_size& pview)
+{
+  if (stub->type() != ST_E_843419)
+    return false;
+
+  typedef AArch64_insn_utilities<big_endian> Insn_utilities;
+  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();
+  Insntype* adrp_view = reinterpret_cast<Insntype*>(
+    pview.view + e843419_stub->adrp_sh_offset());
+  Insntype adrp_insn = adrp_view[0];
+  gold_assert(Insn_utilities::is_adrp(adrp_insn));
+  // Get adrp 33-bit signed imm value.
+  int64_t adrp_imm = Insn_utilities::
+    aarch64_adrp_decode_imm(adrp_insn);
+  // adrp - final value transferred to target register is calculated as:
+  //     PC[11:0] = Zeros(12)
+  //     adrp_dest_value = PC + adrp_imm;
+  int64_t adrp_dest_value = (pc & ~((1 << 12) - 1)) + adrp_imm;
+  // adr -final value transferred to target register is calucalted as:
+  //     PC + adr_imm
+  // So we have:
+  //     PC + adr_imm = adrp_dest_value
+  //   ==>
+  //     adr_imm = adrp_dest_value - PC
+  int64_t adr_imm = adrp_dest_value - pc;
+  // Check if imm fits in adr (21-bit signed).
+  if (-(1 << 20) <= adr_imm && adr_imm < (1 << 20))
+    {
+      // Convert 'adrp' into 'adr'.
+      Insntype adr_insn = adrp_insn & ((1u << 31) - 1);
+      adr_insn = Insn_utilities::
+       aarch64_adr_encode_imm(adr_insn, adr_imm);
+      elfcpp::Swap<32, big_endian>::writeval(adrp_view, adr_insn);
+      return true;
+    }
+  return false;
+}
+
+
 // Relocate sections.
 
 template<int size, bool big_endian>
@@ -1812,7 +2050,8 @@ AArch64_relobj<size, big_endian>::do_relocate_sections(
   if (parameters->options().relocatable())
     return;
 
-  if (parameters->options().fix_cortex_a53_843419())
+  if (parameters->options().fix_cortex_a53_843419()
+      || parameters->options().fix_cortex_a53_835769())
     this->fix_errata(pviews);
 
   Relocate_info<size, big_endian> relinfo;
@@ -1938,11 +2177,11 @@ AArch64_relobj<size, big_endian>::section_needs_reloc_stub_scanning(
 }
 
 
-// Scan section SHNDX for erratum 843419.
+// Scan section SHNDX for erratum 843419 and 835769.
 
 template<int size, bool big_endian>
 void
-AArch64_relobj<size, big_endian>::scan_erratum_843419(
+AArch64_relobj<size, big_endian>::scan_errata(
     unsigned int shndx, const elfcpp::Shdr<size, big_endian>& shdr,
     Output_section* os, const Symbol_table* symtab,
     The_target_aarch64* target)
@@ -1975,10 +2214,6 @@ AArch64_relobj<size, big_endian>::scan_erratum_843419(
   // Find the first mapping symbol record within section shndx.
   typename Mapping_symbol_info::const_iterator p =
     this->mapping_symbol_info_.lower_bound(section_start);
-  if (p == this->mapping_symbol_info_.end() || p->first.shndx_ != shndx)
-    gold_warning(_("cannot scan executable section %u of %s for Cortex-A53 "
-                  "erratum because it has no mapping symbols."),
-                shndx, this->name().c_str());
   while (p != this->mapping_symbol_info_.end() &&
         p->first.shndx_ == shndx)
     {
@@ -1994,7 +2229,18 @@ AArch64_relobj<size, big_endian>::scan_erratum_843419(
            span_end = convert_to_section_size_type(p->first.offset_);
          else
            span_end = convert_to_section_size_type(shdr.get_sh_size());
-         target->scan_erratum_843419_span(
+
+         // Here we do not share the scanning code of both errata. For 843419,
+         // only the last few insns of each page are examined, which is fast,
+         // whereas, for 835769, every insn pair needs to be checked.
+
+         if (parameters->options().fix_cortex_a53_843419())
+           target->scan_erratum_843419_span(
+             this, shndx, span_start, span_end,
+             const_cast<unsigned char*>(input_view), output_address);
+
+         if (parameters->options().fix_cortex_a53_835769())
+           target->scan_erratum_835769_span(
              this, shndx, span_start, span_end,
              const_cast<unsigned char*>(input_view), output_address);
        }
@@ -2035,8 +2281,9 @@ AArch64_relobj<size, big_endian>::scan_sections_for_stubs(
   for (unsigned int i = 1; i < shnum; ++i, p += shdr_size)
     {
       const elfcpp::Shdr<size, big_endian> shdr(p);
-      if (parameters->options().fix_cortex_a53_843419())
-       scan_erratum_843419(i, shdr, out_sections[i], symtab, target);
+      if (parameters->options().fix_cortex_a53_843419()
+         || parameters->options().fix_cortex_a53_835769())
+       scan_errata(i, shdr, out_sections[i], symtab, target);
       if (this->section_needs_reloc_stub_scanning(shdr, out_sections, symtab,
                                                  pshdrs))
        {
@@ -2736,11 +2983,21 @@ class Target_aarch64 : public Sized_target<size, big_endian>
   }
 
 
-  // Scan erratum for a part of a section.
+  // Scan erratum 843419 for a part of a section.
   void
   scan_erratum_843419_span(
     AArch64_relobj<size, big_endian>*,
-    unsigned int shndx,
+    unsigned int,
+    const section_size_type,
+    const section_size_type,
+    unsigned char*,
+    Address);
+
+  // Scan erratum 835769 for a part of a section.
+  void
+  scan_erratum_835769_span(
+    AArch64_relobj<size, big_endian>*,
+    unsigned int,
     const section_size_type,
     const section_size_type,
     unsigned char*,
@@ -3039,12 +3296,29 @@ class Target_aarch64 : public Sized_target<size, big_endian>
     return this->plt_;
   }
 
+  // Helper method to create erratum stubs for ST_E_843419 and ST_E_835769. For
+  // ST_E_843419, we need an additional field for adrp offset.
+  void create_erratum_stub(
+    AArch64_relobj<size, big_endian>* relobj,
+    unsigned int shndx,
+    section_size_type erratum_insn_offset,
+    Address erratum_address,
+    typename Insn_utilities::Insntype erratum_insn,
+    int erratum_type,
+    unsigned int e843419_adrp_offset=0);
+
   // Return whether this is a 3-insn erratum sequence.
   bool is_erratum_843419_sequence(
       typename elfcpp::Swap<32,big_endian>::Valtype insn1,
       typename elfcpp::Swap<32,big_endian>::Valtype insn2,
       typename elfcpp::Swap<32,big_endian>::Valtype insn3);
 
+  // Return whether this is a 835769 sequence.
+  // (Similarly implemented as in elfnn-aarch64.c.)
+  bool is_erratum_835769_sequence(
+      typename elfcpp::Swap<32,big_endian>::Valtype,
+      typename elfcpp::Swap<32,big_endian>::Valtype);
+
   // Get the dynamic reloc section, creating it if necessary.
   Reloc_section*
   rela_dyn_section(Layout*);
@@ -3064,10 +3338,13 @@ class Target_aarch64 : public Sized_target<size, big_endian>
             unsigned int shndx, Output_section* output_section,
             Symbol* sym, const elfcpp::Rela<size, big_endian>& reloc)
   {
+    unsigned int r_type = elfcpp::elf_r_type<size>(reloc.get_r_info());
     this->copy_relocs_.copy_reloc(symtab, layout,
                                  symtab->get_sized_symbol<size>(sym),
                                  object, shndx, output_section,
-                                 reloc, this->rela_dyn_section(layout));
+                                 r_type, reloc.get_r_offset(),
+                                 reloc.get_r_addend(),
+                                 this->rela_dyn_section(layout));
   }
 
   // Information about this specific target which we pass to the
@@ -3150,7 +3427,7 @@ const Target::Target_info Target_aarch64<64, false>::aarch64_info =
   '\0',                        // wrap_char
   "/lib/ld.so.1",      // program interpreter
   0x400000,            // default_text_segment_address
-  0x1000             // abi_pagesize (overridable by -z max-page-size)
+  0x10000,             // abi_pagesize (overridable by -z max-page-size)
   0x1000,              // common_pagesize (overridable by -z common-page-size)
   false,                // isolate_execinstr
   0,                    // rosegment_gap
@@ -3160,7 +3437,8 @@ const Target::Target_info Target_aarch64<64, false>::aarch64_info =
   0,                   // large_common_section_flags
   NULL,                        // attributes_section
   NULL,                        // attributes_vendor
-  "_start"             // entry_symbol_name
+  "_start",            // entry_symbol_name
+  32,                  // hash_entry_size
 };
 
 template<>
@@ -3177,7 +3455,7 @@ const Target::Target_info Target_aarch64<32, false>::aarch64_info =
   '\0',                        // wrap_char
   "/lib/ld.so.1",      // program interpreter
   0x400000,            // default_text_segment_address
-  0x1000             // abi_pagesize (overridable by -z max-page-size)
+  0x10000,             // abi_pagesize (overridable by -z max-page-size)
   0x1000,              // common_pagesize (overridable by -z common-page-size)
   false,                // isolate_execinstr
   0,                    // rosegment_gap
@@ -3187,7 +3465,8 @@ const Target::Target_info Target_aarch64<32, false>::aarch64_info =
   0,                   // large_common_section_flags
   NULL,                        // attributes_section
   NULL,                        // attributes_vendor
-  "_start"             // entry_symbol_name
+  "_start",            // entry_symbol_name
+  32,                  // hash_entry_size
 };
 
 template<>
@@ -3204,7 +3483,7 @@ const Target::Target_info Target_aarch64<64, true>::aarch64_info =
   '\0',                        // wrap_char
   "/lib/ld.so.1",      // program interpreter
   0x400000,            // default_text_segment_address
-  0x1000             // abi_pagesize (overridable by -z max-page-size)
+  0x10000,             // abi_pagesize (overridable by -z max-page-size)
   0x1000,              // common_pagesize (overridable by -z common-page-size)
   false,                // isolate_execinstr
   0,                    // rosegment_gap
@@ -3214,7 +3493,8 @@ const Target::Target_info Target_aarch64<64, true>::aarch64_info =
   0,                   // large_common_section_flags
   NULL,                        // attributes_section
   NULL,                        // attributes_vendor
-  "_start"             // entry_symbol_name
+  "_start",            // entry_symbol_name
+  32,                  // hash_entry_size
 };
 
 template<>
@@ -3231,7 +3511,7 @@ const Target::Target_info Target_aarch64<32, true>::aarch64_info =
   '\0',                        // wrap_char
   "/lib/ld.so.1",      // program interpreter
   0x400000,            // default_text_segment_address
-  0x1000             // abi_pagesize (overridable by -z max-page-size)
+  0x10000,             // abi_pagesize (overridable by -z max-page-size)
   0x1000,              // common_pagesize (overridable by -z common-page-size)
   false,                // isolate_execinstr
   0,                    // rosegment_gap
@@ -3241,7 +3521,8 @@ const Target::Target_info Target_aarch64<32, true>::aarch64_info =
   0,                   // large_common_section_flags
   NULL,                        // attributes_section
   NULL,                        // attributes_vendor
-  "_start"             // entry_symbol_name
+  "_start",            // entry_symbol_name
+  32,                  // hash_entry_size
 };
 
 // Get the GOT section, creating it if necessary.
@@ -5713,6 +5994,29 @@ Target_aarch64<size, big_endian>::Scan::local(
     case elfcpp::R_AARCH64_PREL16:
       break;
 
+    case elfcpp::R_AARCH64_ADR_GOT_PAGE:
+    case elfcpp::R_AARCH64_LD64_GOT_LO12_NC:
+      // This pair of relocations is used to access a specific GOT entry.
+      {
+       bool is_new = false;
+       // This symbol requires a GOT entry.
+       if (is_ifunc)
+         is_new = got->add_local_plt(object, r_sym, GOT_TYPE_STANDARD);
+       else
+         is_new = got->add_local(object, r_sym, GOT_TYPE_STANDARD);
+       if (is_new && parameters->options().output_is_position_independent())
+         target->rela_dyn_section(layout)->
+           add_local_relative(object,
+                              r_sym,
+                              elfcpp::R_AARCH64_RELATIVE,
+                              got,
+                              object->local_got_offset(r_sym,
+                                                       GOT_TYPE_STANDARD),
+                              0,
+                              false);
+      }
+      break;
+
     case elfcpp::R_AARCH64_LD_PREL_LO19:        // 273
     case elfcpp::R_AARCH64_ADR_PREL_LO21:       // 274
     case elfcpp::R_AARCH64_ADR_PREL_PG_HI21:    // 275
@@ -6610,16 +6914,41 @@ Target_aarch64<size, big_endian>::Relocate::relocate(
       break;
 
     case elfcpp::R_AARCH64_ABS64:
+      if (!parameters->options().apply_dynamic_relocs()
+          && parameters->options().output_is_position_independent()
+          && gsym != NULL
+          && gsym->needs_dynamic_reloc(reloc_property->reference_flags())
+          && !gsym->can_use_relative_reloc(false))
+        // We have generated an absolute dynamic relocation, so do not
+        // apply the relocation statically. (Works around bugs in older
+        // Android dynamic linkers.)
+        break;
       reloc_status = Reloc::template rela_ua<64>(
        view, object, psymval, addend, reloc_property);
       break;
 
     case elfcpp::R_AARCH64_ABS32:
+      if (!parameters->options().apply_dynamic_relocs()
+          && parameters->options().output_is_position_independent()
+          && gsym != NULL
+          && gsym->needs_dynamic_reloc(reloc_property->reference_flags()))
+        // We have generated an absolute dynamic relocation, so do not
+        // apply the relocation statically. (Works around bugs in older
+        // Android dynamic linkers.)
+        break;
       reloc_status = Reloc::template rela_ua<32>(
        view, object, psymval, addend, reloc_property);
       break;
 
     case elfcpp::R_AARCH64_ABS16:
+      if (!parameters->options().apply_dynamic_relocs()
+          && parameters->options().output_is_position_independent()
+          && gsym != NULL
+          && gsym->needs_dynamic_reloc(reloc_property->reference_flags()))
+        // We have generated an absolute dynamic relocation, so do not
+        // apply the relocation statically. (Works around bugs in older
+        // Android dynamic linkers.)
+        break;
       reloc_status = Reloc::template rela_ua<16>(
        view, object, psymval, addend, reloc_property);
       break;
@@ -7673,6 +8002,144 @@ Target_aarch64<size, big_endian>::is_erratum_843419_sequence(
 }
 
 
+// Return whether this is a 835769 sequence.
+// (Similarly implemented as in elfnn-aarch64.c.)
+
+template<int size, bool big_endian>
+bool
+Target_aarch64<size, big_endian>::is_erratum_835769_sequence(
+    typename elfcpp::Swap<32,big_endian>::Valtype insn1,
+    typename elfcpp::Swap<32,big_endian>::Valtype insn2)
+{
+  uint32_t rt;
+  uint32_t rt2;
+  uint32_t rn;
+  uint32_t rm;
+  uint32_t ra;
+  bool pair;
+  bool load;
+
+  if (Insn_utilities::aarch64_mlxl(insn2)
+      && Insn_utilities::aarch64_mem_op_p (insn1, &rt, &rt2, &pair, &load))
+    {
+      /* Any SIMD memory op is independent of the subsequent MLA
+        by definition of the erratum.  */
+      if (Insn_utilities::aarch64_bit(insn1, 26))
+       return true;
+
+      /* If not SIMD, check for integer memory ops and MLA relationship.  */
+      rn = Insn_utilities::aarch64_rn(insn2);
+      ra = Insn_utilities::aarch64_ra(insn2);
+      rm = Insn_utilities::aarch64_rm(insn2);
+
+      /* If this is a load and there's a true(RAW) dependency, we are safe
+        and this is not an erratum sequence.  */
+      if (load &&
+         (rt == rn || rt == rm || rt == ra
+          || (pair && (rt2 == rn || rt2 == rm || rt2 == ra))))
+       return false;
+
+      /* We conservatively put out stubs for all other cases (including
+        writebacks).  */
+      return true;
+    }
+
+  return false;
+}
+
+
+// Helper method to create erratum stub for ST_E_843419 and ST_E_835769.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::create_erratum_stub(
+    AArch64_relobj<size, big_endian>* relobj,
+    unsigned int shndx,
+    section_size_type erratum_insn_offset,
+    Address erratum_address,
+    typename Insn_utilities::Insntype erratum_insn,
+    int erratum_type,
+    unsigned int e843419_adrp_offset)
+{
+  gold_assert(erratum_type == ST_E_843419 || erratum_type == ST_E_835769);
+  The_stub_table* stub_table = relobj->stub_table(shndx);
+  gold_assert(stub_table != NULL);
+  if (stub_table->find_erratum_stub(relobj,
+                                   shndx,
+                                   erratum_insn_offset) == NULL)
+    {
+      const int BPI = AArch64_insn_utilities<big_endian>::BYTES_PER_INSN;
+      The_erratum_stub* stub;
+      if (erratum_type == ST_E_835769)
+       stub = new The_erratum_stub(relobj, erratum_type, shndx,
+                                   erratum_insn_offset);
+      else if (erratum_type == ST_E_843419)
+       stub = new E843419_stub<size, big_endian>(
+           relobj, shndx, erratum_insn_offset, e843419_adrp_offset);
+      else
+       gold_unreachable();
+      stub->set_erratum_insn(erratum_insn);
+      stub->set_erratum_address(erratum_address);
+      // For erratum ST_E_843419 and ST_E_835769, the destination address is
+      // always the next insn after erratum insn.
+      stub->set_destination_address(erratum_address + BPI);
+      stub_table->add_erratum_stub(stub);
+    }
+}
+
+
+// Scan erratum for section SHNDX range [output_address + span_start,
+// output_address + span_end). Note here we do not share the code with
+// scan_erratum_843419_span function, because for 843419 we optimize by only
+// scanning the last few insns of a page, whereas for 835769, we need to scan
+// every insn.
+
+template<int size, bool big_endian>
+void
+Target_aarch64<size, big_endian>::scan_erratum_835769_span(
+    AArch64_relobj<size, big_endian>*  relobj,
+    unsigned int shndx,
+    const section_size_type span_start,
+    const section_size_type span_end,
+    unsigned char* input_view,
+    Address output_address)
+{
+  typedef typename Insn_utilities::Insntype Insntype;
+
+  const int BPI = AArch64_insn_utilities<big_endian>::BYTES_PER_INSN;
+
+  // Adjust output_address and view to the start of span.
+  output_address += span_start;
+  input_view += span_start;
+
+  section_size_type span_length = span_end - span_start;
+  section_size_type offset = 0;
+  for (offset = 0; offset + BPI < span_length; offset += BPI)
+    {
+      Insntype* ip = reinterpret_cast<Insntype*>(input_view + offset);
+      Insntype insn1 = ip[0];
+      Insntype insn2 = ip[1];
+      if (is_erratum_835769_sequence(insn1, insn2))
+       {
+         Insntype erratum_insn = insn2;
+         // "span_start + offset" is the offset for insn1. So for insn2, it is
+         // "span_start + offset + BPI".
+         section_size_type erratum_insn_offset = span_start + offset + BPI;
+         Address erratum_address = output_address + offset + BPI;
+         gold_info(_("Erratum 835769 found and fixed at \"%s\", "
+                        "section %d, offset 0x%08x."),
+                      relobj->name().c_str(), shndx,
+                      (unsigned int)(span_start + offset));
+
+         this->create_erratum_stub(relobj, shndx,
+                                   erratum_insn_offset, erratum_address,
+                                   erratum_insn, ST_E_835769);
+         offset += BPI;  // Skip mac insn.
+       }
+    }
+}  // End of "Target_aarch64::scan_erratum_835769_span".
+
+
 // Scan erratum for section SHNDX range
 // [output_address + span_start, output_address + span_end).
 
@@ -7745,32 +8212,18 @@ Target_aarch64<size, big_endian>::scan_erratum_843419_span(
            }
          if (do_report)
            {
-             gold_warning(_("Erratum 843419 found and fixed at \"%s\", "
+             gold_info(_("Erratum 843419 found and fixed at \"%s\", "
                             "section %d, offset 0x%08x."),
                           relobj->name().c_str(), shndx,
                           (unsigned int)(span_start + offset));
-             unsigned int errata_insn_offset =
+             unsigned int erratum_insn_offset =
                span_start + offset + insn_offset;
-             The_stub_table* stub_table = relobj->stub_table(shndx);
-             gold_assert(stub_table != NULL);
-             if (stub_table->find_erratum_stub(relobj,
-                                               shndx,
-                                               errata_insn_offset) == NULL)
-               {
-                 The_erratum_stub* stub = new The_erratum_stub(
-                     relobj, ST_E_843419, shndx,
-                     errata_insn_offset);
-                 Address erratum_address =
-                   output_address + offset + insn_offset;
-                 // Stub destination address is the next insn after the
-                 // erratum.
-                 Address dest_address = erratum_address
-                   + Insn_utilities::BYTES_PER_INSN;
-                 stub->set_erratum_insn(erratum_insn);
-                 stub->set_erratum_address(erratum_address);
-                 stub->set_destination_address(dest_address);
-                 stub_table->add_erratum_stub(stub);
-               }
+             Address erratum_address =
+               output_address + offset + insn_offset;
+             create_erratum_stub(relobj, shndx,
+                                 erratum_insn_offset, erratum_address,
+                                 erratum_insn, ST_E_843419,
+                                 span_start + offset);
            }
        }
 
This page took 0.058479 seconds and 4 git commands to generate.