]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - opcodes/aarch64-dis.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / opcodes / aarch64-dis.c
index fc7e95d066807f2909d1a1c891d7a89a5f31204b..da1c96dadc1e4a2fadc06a654b27c622b102f32e 100644 (file)
@@ -1,5 +1,5 @@
 /* aarch64-dis.c -- AArch64 disassembler.
-   Copyright (C) 2009-2019 Free Software Foundation, Inc.
+   Copyright (C) 2009-2021 Free Software Foundation, Inc.
    Contributed by ARM Ltd.
 
    This file is part of the GNU opcodes library.
@@ -35,6 +35,7 @@ enum map_type
   MAP_DATA
 };
 
+static aarch64_feature_set arch_variant; /* See select_aarch64_variant.  */
 static enum map_type last_type;
 static int last_mapping_sym = -1;
 static bfd_vma last_stop_offset = 0;
@@ -178,18 +179,15 @@ extract_all_fields (const aarch64_operand *self, aarch64_insn code)
 }
 
 /* Sign-extend bit I of VALUE.  */
-static inline int32_t
+static inline uint64_t
 sign_extend (aarch64_insn value, unsigned i)
 {
-  uint32_t ret = value;
+  uint64_t ret, sign;
 
   assert (i < 32);
-  if ((value >> i) & 0x1)
-    {
-      uint32_t val = (uint32_t)(-1) << i;
-      ret = ret | val;
-    }
-  return (int32_t) ret;
+  ret = value;
+  sign = (uint64_t) 1 << i;
+  return ((ret & (sign + sign - 1)) ^ sign) - sign;
 }
 
 /* N.B. the following inline helpfer functions create a dependency on the
@@ -254,6 +252,16 @@ get_expected_qualifier (const aarch64_inst *inst, int i)
 
 /* Operand extractors.  */
 
+bfd_boolean
+aarch64_ext_none (const aarch64_operand *self ATTRIBUTE_UNUSED,
+                 aarch64_opnd_info *info ATTRIBUTE_UNUSED,
+                 const aarch64_insn code ATTRIBUTE_UNUSED,
+                 const aarch64_inst *inst ATTRIBUTE_UNUSED,
+                 aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+  return TRUE;
+}
+
 bfd_boolean
 aarch64_ext_regno (const aarch64_operand *self, aarch64_opnd_info *info,
                   const aarch64_insn code,
@@ -348,6 +356,7 @@ aarch64_ext_reglane (const aarch64_operand *self, aarch64_opnd_info *info,
       switch (info->qualifier)
        {
        case AARCH64_OPND_QLF_S_4B:
+       case AARCH64_OPND_QLF_S_2H:
          /* L:H */
          info->reglane.index = extract_fields (code, 0, 2, FLD_H, FLD_L);
          info->reglane.regno &= 0x1f;
@@ -657,7 +666,7 @@ aarch64_ext_imm (const aarch64_operand *self, aarch64_opnd_info *info,
                 const aarch64_inst *inst ATTRIBUTE_UNUSED,
                 aarch64_operand_error *errors ATTRIBUTE_UNUSED)
 {
-  int64_t imm;
+  uint64_t imm;
 
   imm = extract_all_fields (self, code);
 
@@ -1284,6 +1293,21 @@ aarch64_ext_barrier (const aarch64_operand *self ATTRIBUTE_UNUSED,
   return TRUE;
 }
 
+/* Decode the memory barrier option operand for DSB <option>nXS|#<imm>.  */
+
+bfd_boolean
+aarch64_ext_barrier_dsb_nxs (const aarch64_operand *self ATTRIBUTE_UNUSED,
+                    aarch64_opnd_info *info,
+                    aarch64_insn code,
+                    const aarch64_inst *inst ATTRIBUTE_UNUSED,
+                    aarch64_operand_error *errors ATTRIBUTE_UNUSED)
+{
+  /* For the DSB nXS barrier variant immediate is encoded in 2-bit field.  */
+  aarch64_insn field = extract_field (FLD_CRm_dsb_nxs, code, 0);
+  info->barrier = aarch64_barrier_dsb_nxs_options + field;
+  return TRUE;
+}
+
 /* Decode the prefetch operation option operand for e.g.
      PRFM <prfop>, [<Xn|SP>{, #<pimm>}].  */
 
@@ -2682,6 +2706,13 @@ determine_disassembling_preference (struct aarch64_inst *inst,
          DEBUG_TRACE ("skip %s as base opcode not match", alias->name);
          continue;
        }
+
+      if (!AARCH64_CPU_HAS_FEATURE (arch_variant, *alias->avariant))
+       {
+         DEBUG_TRACE ("skip %s: we're missing features", alias->name);
+         continue;
+       }
+
       /* No need to do any complicated transformation on operands, if the alias
         opcode does not have any operand.  */
       if (aarch64_num_of_operands (alias) == 0 && alias->opcode == inst->value)
@@ -2806,10 +2837,64 @@ aarch64_decode_variant_using_iclass (aarch64_inst *inst)
       variant = i - 1;
       break;
 
+    case sve_size_bh:
     case sve_size_sd:
       variant = extract_field (FLD_SVE_sz, inst->value, 0);
       break;
 
+    case sve_size_sd2:
+      variant = extract_field (FLD_SVE_sz2, inst->value, 0);
+      break;
+
+    case sve_size_hsd2:
+      i = extract_field (FLD_SVE_size, inst->value, 0);
+      if (i < 1)
+       return FALSE;
+      variant = i - 1;
+      break;
+
+    case sve_size_13:
+      /* Ignore low bit of this field since that is set in the opcode for
+        instructions of this iclass.  */
+      i = (extract_field (FLD_size, inst->value, 0) & 2);
+      variant = (i >> 1);
+      break;
+
+    case sve_shift_tsz_bhsd:
+      i = extract_fields (inst->value, 0, 2, FLD_SVE_tszh, FLD_SVE_tszl_19);
+      if (i == 0)
+       return FALSE;
+      while (i != 1)
+       {
+         i >>= 1;
+         variant += 1;
+       }
+      break;
+
+    case sve_size_tsz_bhs:
+      i = extract_fields (inst->value, 0, 2, FLD_SVE_sz, FLD_SVE_tszl_19);
+      if (i == 0)
+       return FALSE;
+      while (i != 1)
+       {
+         if (i & 1)
+           return FALSE;
+         i >>= 1;
+         variant += 1;
+       }
+      break;
+
+    case sve_shift_tsz_hsd:
+      i = extract_fields (inst->value, 0, 2, FLD_SVE_sz, FLD_SVE_tszl_19);
+      if (i == 0)
+       return FALSE;
+      while (i != 1)
+       {
+         i >>= 1;
+         variant += 1;
+       }
+      break;
+
     default:
       /* No mapping between instruction class and qualifiers.  */
       return TRUE;
@@ -2920,7 +3005,7 @@ aarch64_opcode_decode (const aarch64_opcode *opcode, const aarch64_insn code,
       DEBUG_TRACE ("constraint matching FAIL");
     }
 
-decode_fail:
+ decode_fail:
   return FALSE;
 }
 \f
@@ -3012,7 +3097,7 @@ print_operands (bfd_vma pc, const aarch64_opcode *opcode,
 
       /* Generate the operand string in STR.  */
       aarch64_print_operand (str, sizeof (str), pc, opcode, opnds, i, &pcrel_p,
-                            &info->target, &notes);
+                            &info->target, &notes, arch_variant);
 
       /* Print the delimiter (taking account of omitted operand(s)).  */
       if (str[0] != '\0')
@@ -3259,6 +3344,7 @@ static int
 get_sym_code_type (struct disassemble_info *info, int n,
                   enum map_type *map_type)
 {
+  asymbol * as;
   elf_symbol_type *es;
   unsigned int type;
   const char *name;
@@ -3267,7 +3353,14 @@ get_sym_code_type (struct disassemble_info *info, int n,
   if (info->section != NULL && info->section != info->symtab[n]->section)
     return FALSE;
 
-  es = *(elf_symbol_type **)(info->symtab + n);
+  if (n >= info->symtab_size)
+    return FALSE;
+
+  as = info->symtab[n];
+  if (bfd_asymbol_flavour (as) != bfd_target_elf_flavour)
+    return FALSE;
+  es = (elf_symbol_type *) as;
+
   type = ELF_ST_TYPE (es->internal_elf_sym.st_info);
 
   /* If the symbol has function type then use that.  */
@@ -3290,6 +3383,24 @@ get_sym_code_type (struct disassemble_info *info, int n,
   return FALSE;
 }
 
+/* Set the feature bits in arch_variant in order to get the correct disassembly
+   for the chosen architecture variant.
+
+   Currently we only restrict disassembly for Armv8-R and otherwise enable all
+   non-R-profile features.  */
+static void
+select_aarch64_variant (unsigned mach)
+{
+  switch (mach)
+    {
+    case bfd_mach_aarch64_8R:
+      arch_variant = AARCH64_ARCH_V8_R;
+      break;
+    default:
+      arch_variant = AARCH64_ANY & ~(AARCH64_FEATURE_V8_R);
+    }
+}
+
 /* Entry-point of the AArch64 disassembler.  */
 
 int
@@ -3304,6 +3415,7 @@ print_insn_aarch64 (bfd_vma pc,
   unsigned int size = 4;
   unsigned long        data;
   aarch64_operand_error errors;
+  static bfd_boolean set_features;
 
   if (info->disassembler_options)
     {
@@ -3315,17 +3427,35 @@ print_insn_aarch64 (bfd_vma pc,
       info->disassembler_options = NULL;
     }
 
+  if (!set_features)
+    {
+      select_aarch64_variant (info->mach);
+      set_features = TRUE;
+    }
+
   /* Aarch64 instructions are always little-endian */
   info->endian_code = BFD_ENDIAN_LITTLE;
 
+  /* Default to DATA.  A text section is required by the ABI to contain an
+     INSN mapping symbol at the start.  A data section has no such
+     requirement, hence if no mapping symbol is found the section must
+     contain only data.  This however isn't very useful if the user has
+     fully stripped the binaries.  If this is the case use the section
+     attributes to determine the default.  If we have no section default to
+     INSN as well, as we may be disassembling some raw bytes on a baremetal
+     HEX file or similar.  */
+  enum map_type type = MAP_DATA;
+  if ((info->section && info->section->flags & SEC_CODE) || !info->section)
+    type = MAP_INSN;
+
   /* First check the full symtab for a mapping symbol, even if there
      are no usable non-mapping symbols for this address.  */
   if (info->symtab_size != 0
       && bfd_asymbol_flavour (*info->symtab) == bfd_target_elf_flavour)
     {
-      enum map_type type = MAP_INSN;
       int last_sym = -1;
-      bfd_vma addr;
+      bfd_vma addr, section_vma = 0;
+      bfd_boolean can_use_search_opt_p;
       int n;
 
       if (pc <= last_mapping_addr)
@@ -3334,13 +3464,20 @@ print_insn_aarch64 (bfd_vma pc,
       /* Start scanning at the start of the function, or wherever
         we finished last time.  */
       n = info->symtab_pos + 1;
+
       /* If the last stop offset is different from the current one it means we
         are disassembling a different glob of bytes.  As such the optimization
         would not be safe and we should start over.  */
-      if (n < last_mapping_sym && info->stop_offset == last_stop_offset)
+      can_use_search_opt_p = last_mapping_sym >= 0
+                            && info->stop_offset == last_stop_offset;
+
+      if (n >= last_mapping_sym && can_use_search_opt_p)
        n = last_mapping_sym;
 
-      /* Scan up to the location being disassembled.  */
+      /* Look down while we haven't passed the location being disassembled.
+        The reason for this is that there's no defined order between a symbol
+        and an mapping symbol that may be at the same address.  We may have to
+        look at least one position ahead.  */
       for (; n < info->symtab_size; n++)
        {
          addr = bfd_asymbol_value (info->symtab[n]);
@@ -3356,13 +3493,24 @@ print_insn_aarch64 (bfd_vma pc,
       if (!found)
        {
          n = info->symtab_pos;
-         if (n < last_mapping_sym)
+         if (n >= last_mapping_sym && can_use_search_opt_p)
            n = last_mapping_sym;
 
          /* No mapping symbol found at this address.  Look backwards
-            for a preceeding one.  */
+            for a preceeding one, but don't go pass the section start
+            otherwise a data section with no mapping symbol can pick up
+            a text mapping symbol of a preceeding section.  The documentation
+            says section can be NULL, in which case we will seek up all the
+            way to the top.  */
+         if (info->section)
+           section_vma = info->section->vma;
+
          for (; n >= 0; n--)
            {
+             addr = bfd_asymbol_value (info->symtab[n]);
+             if (addr < section_vma)
+               break;
+
              if (get_sym_code_type (info, n, &type))
                {
                  last_sym = n;
@@ -3400,8 +3548,11 @@ print_insn_aarch64 (bfd_vma pc,
            size = (pc & 1) ? 1 : 2;
        }
     }
+  else
+    last_type = type;
 
-  if (last_type == MAP_DATA)
+  /* PR 10263: Disassemble data if requested to do so by the user.  */
+  if (last_type == MAP_DATA && ((info->flags & DISASSEMBLE_DATA) == 0))
     {
       /* size was set above.  */
       info->bytes_per_chunk = size;