Automatic date update in version.in
[binutils-gdb.git] / opcodes / ppc-dis.c
index 841ba092b85ac49d5da1d6ebf283a7c6282d799a..45e8faeef5e55738546c5233032765072f0ce2b3 100644 (file)
@@ -1,5 +1,5 @@
 /* ppc-dis.c -- Disassemble PowerPC instructions
-   Copyright (C) 1994-2021 Free Software Foundation, Inc.
+   Copyright (C) 1994-2022 Free Software Foundation, Inc.
    Written by Ian Lance Taylor, Cygnus Support
 
    This file is part of the GNU opcodes library.
@@ -40,10 +40,20 @@ struct dis_private
 {
   /* Stash the result of parsing disassembler_options here.  */
   ppc_cpu_t dialect;
+
+  /* .got and .plt sections.  NAME is set to NULL if not present.  */
+  struct sec_buf {
+    asection *sec;
+    bfd_byte *buf;
+    const char *name;
+  } special[2];
 };
 
-#define POWERPC_DIALECT(INFO) \
-  (((struct dis_private *) ((INFO)->private_data))->dialect)
+static inline struct dis_private *
+private_data (struct disassemble_info *info)
+{
+  return (struct dis_private *) info->private_data;
+}
 
 struct ppc_mopt {
   /* Option string, without -m or -M prefix.  */
@@ -270,7 +280,7 @@ get_powerpc_dialect (struct disassemble_info *info)
   ppc_cpu_t dialect = 0;
 
   if (info->private_data)
-    dialect = POWERPC_DIALECT (info);
+    dialect = private_data (info)->dialect;
 
   /* Disassemble according to the section headers flags for VLE-mode.  */
   if (dialect & PPC_OPCODE_VLE
@@ -387,7 +397,7 @@ powerpc_init_dialect (struct disassemble_info *info)
     }
 
   info->private_data = priv;
-  POWERPC_DIALECT(info) = dialect;
+  private_data (info)->dialect = dialect;
 }
 
 #define PPC_OPCD_SEGS (1 + PPC_OP (-1))
@@ -399,26 +409,26 @@ static unsigned short vle_opcd_indices[VLE_OPCD_SEGS + 1];
 #define SPE2_OPCD_SEGS (1 + SPE2_XOP_TO_SEG (SPE2_XOP (-1)))
 static unsigned short spe2_opcd_indices[SPE2_OPCD_SEGS + 1];
 
-static bfd_boolean
+static bool
 ppc_symbol_is_valid (asymbol *sym,
                     struct disassemble_info *info ATTRIBUTE_UNUSED)
 {
   elf_symbol_type * est;
 
   if (sym == NULL)
-    return FALSE;
+    return false;
 
   est = elf_symbol_from (sym);
-  
+
   /* Ignore ELF hidden, local, no-type symbols.
      These are generated by annobin.  */
   if (est != NULL
       && ELF_ST_VISIBILITY (est->internal_elf_sym.st_other) == STV_HIDDEN
       && ELF_ST_BIND (est->internal_elf_sym.st_info) == STB_LOCAL
       && ELF_ST_TYPE (est->internal_elf_sym.st_info) == STT_NOTYPE)
-    return FALSE;
+    return false;
 
-  return TRUE;
+  return true;
 }
 
 /* Calculate opcode table indices to speed up disassembly,
@@ -477,6 +487,11 @@ disassemble_init_powerpc (struct disassemble_info *info)
     }
 
   powerpc_init_dialect (info);
+  if (info->private_data != NULL)
+    {
+      private_data (info)->special[0].name = ".got";
+      private_data (info)->special[1].name = ".plt";
+    }
 }
 
 /* Print a big endian PowerPC instruction.  */
@@ -530,9 +545,9 @@ operand_value_powerpc (const struct powerpc_operand *operand,
 
 /* Determine whether the optional operand(s) should be printed.  */
 
-static bfd_boolean
-skip_optional_operands (const unsigned char *opindex,
-                       uint64_t insn, ppc_cpu_t dialect)
+static bool
+skip_optional_operands (const ppc_opindex_t *opindex,
+                       uint64_t insn, ppc_cpu_t dialect, bool *is_pcrel)
 {
   const struct powerpc_operand *operand;
   int num_optional;
@@ -541,19 +556,23 @@ skip_optional_operands (const unsigned char *opindex,
     {
       operand = &powerpc_operands[*opindex];
       if ((operand->flags & PPC_OPERAND_NEXT) != 0)
-       return FALSE;
+       return false;
       if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0)
        {
+         int64_t value = operand_value_powerpc (operand, insn, dialect);
+
+         if (operand->shift == 52)
+           *is_pcrel = value != 0;
+
          /* Negative count is used as a flag to extract function.  */
          --num_optional;
-         if (operand_value_powerpc (operand, insn, dialect)
-             != ppc_optional_operand_value (operand, insn, dialect,
-                                            num_optional))
-           return FALSE;
+         if (value != ppc_optional_operand_value (operand, insn, dialect,
+                                                  num_optional))
+           return false;
        }
     }
 
-  return TRUE;
+  return true;
 }
 
 /* Find a match for INSN in the opcode table, given machine DIALECT.  */
@@ -561,7 +580,7 @@ skip_optional_operands (const unsigned char *opindex,
 static const struct powerpc_opcode *
 lookup_powerpc (uint64_t insn, ppc_cpu_t dialect)
 {
-  const struct powerpc_opcode *opcode, *opcode_end, *last;
+  const struct powerpc_opcode *opcode, *opcode_end;
   unsigned long op;
 
   /* Get the major opcode of the instruction.  */
@@ -569,19 +588,19 @@ lookup_powerpc (uint64_t insn, ppc_cpu_t dialect)
 
   /* Find the first match in the opcode table for this major opcode.  */
   opcode_end = powerpc_opcodes + powerpc_opcd_indices[op + 1];
-  last = NULL;
   for (opcode = powerpc_opcodes + powerpc_opcd_indices[op];
        opcode < opcode_end;
        ++opcode)
     {
-      const unsigned char *opindex;
+      const ppc_opindex_t *opindex;
       const struct powerpc_operand *operand;
       int invalid;
 
       if ((insn & opcode->mask) != opcode->opcode
          || ((dialect & PPC_OPCODE_ANY) == 0
              && ((opcode->flags & dialect) == 0
-                 || (opcode->deprecated & dialect) != 0)))
+                 || (opcode->deprecated & dialect) != 0))
+         || (opcode->deprecated & dialect & PPC_OPCODE_RAW) != 0)
        continue;
 
       /* Check validity of operands.  */
@@ -595,16 +614,10 @@ lookup_powerpc (uint64_t insn, ppc_cpu_t dialect)
       if (invalid)
        continue;
 
-      if ((dialect & PPC_OPCODE_RAW) == 0)
-       return opcode;
-
-      /* The raw machine insn is one that is not a specialization.  */
-      if (last == NULL
-         || (last->mask & ~opcode->mask) != 0)
-       last = opcode;
+      return opcode;
     }
 
-  return last;
+  return NULL;
 }
 
 /* Find a match for INSN in the PREFIX opcode table.  */
@@ -612,7 +625,7 @@ lookup_powerpc (uint64_t insn, ppc_cpu_t dialect)
 static const struct powerpc_opcode *
 lookup_prefix (uint64_t insn, ppc_cpu_t dialect)
 {
-  const struct powerpc_opcode *opcode, *opcode_end, *last;
+  const struct powerpc_opcode *opcode, *opcode_end;
   unsigned long seg;
 
   /* Get the opcode segment of the instruction.  */
@@ -620,19 +633,18 @@ lookup_prefix (uint64_t insn, ppc_cpu_t dialect)
 
   /* Find the first match in the opcode table for this major opcode.  */
   opcode_end = prefix_opcodes + prefix_opcd_indices[seg + 1];
-  last = NULL;
   for (opcode = prefix_opcodes + prefix_opcd_indices[seg];
        opcode < opcode_end;
        ++opcode)
     {
-      const unsigned char *opindex;
+      const ppc_opindex_t *opindex;
       const struct powerpc_operand *operand;
       int invalid;
 
       if ((insn & opcode->mask) != opcode->opcode
          || ((dialect & PPC_OPCODE_ANY) == 0
-             && ((opcode->flags & dialect) == 0
-                 || (opcode->deprecated & dialect) != 0)))
+             && (opcode->flags & dialect) == 0)
+         || (opcode->deprecated & dialect) != 0)
        continue;
 
       /* Check validity of operands.  */
@@ -646,22 +658,16 @@ lookup_prefix (uint64_t insn, ppc_cpu_t dialect)
       if (invalid)
        continue;
 
-      if ((dialect & PPC_OPCODE_RAW) == 0)
-       return opcode;
-
-      /* The raw machine insn is one that is not a specialization.  */
-      if (last == NULL
-         || (last->mask & ~opcode->mask) != 0)
-       last = opcode;
+      return opcode;
     }
 
-  return last;
+  return NULL;
 }
 
 /* Find a match for INSN in the VLE opcode table.  */
 
 static const struct powerpc_opcode *
-lookup_vle (uint64_t insn)
+lookup_vle (uint64_t insn, ppc_cpu_t dialect)
 {
   const struct powerpc_opcode *opcode;
   const struct powerpc_opcode *opcode_end;
@@ -683,16 +689,17 @@ lookup_vle (uint64_t insn)
     {
       uint64_t table_opcd = opcode->opcode;
       uint64_t table_mask = opcode->mask;
-      bfd_boolean table_op_is_short = PPC_OP_SE_VLE(table_mask);
+      bool table_op_is_short = PPC_OP_SE_VLE(table_mask);
       uint64_t insn2;
-      const unsigned char *opindex;
+      const ppc_opindex_t *opindex;
       const struct powerpc_operand *operand;
       int invalid;
 
       insn2 = insn;
       if (table_op_is_short)
        insn2 >>= 16;
-      if ((insn2 & table_mask) != table_opcd)
+      if ((insn2 & table_mask) != table_opcd
+         || (opcode->deprecated & dialect) != 0)
        continue;
 
       /* Check validity of operands.  */
@@ -715,7 +722,7 @@ lookup_vle (uint64_t insn)
 /* Find a match for INSN in the SPE2 opcode table.  */
 
 static const struct powerpc_opcode *
-lookup_spe2 (uint64_t insn)
+lookup_spe2 (uint64_t insn, ppc_cpu_t dialect)
 {
   const struct powerpc_opcode *opcode, *opcode_end;
   unsigned op, xop, seg;
@@ -739,12 +746,13 @@ lookup_spe2 (uint64_t insn)
       uint64_t table_opcd = opcode->opcode;
       uint64_t table_mask = opcode->mask;
       uint64_t insn2;
-      const unsigned char *opindex;
+      const ppc_opindex_t *opindex;
       const struct powerpc_operand *operand;
       int invalid;
 
       insn2 = insn;
-      if ((insn2 & table_mask) != table_opcd)
+      if ((insn2 & table_mask) != table_opcd
+         || (opcode->deprecated & dialect) != 0)
        continue;
 
       /* Check validity of operands.  */
@@ -764,6 +772,75 @@ lookup_spe2 (uint64_t insn)
   return NULL;
 }
 
+static arelent *
+bsearch_reloc (arelent **lo, arelent **hi, bfd_vma vma)
+{
+  while (lo < hi)
+    {
+      arelent **mid = lo + (hi - lo) / 2;
+      arelent *rel = *mid;
+
+      if (vma < rel->address)
+       hi = mid;
+      else if (vma > rel->address)
+       lo = mid + 1;
+      else
+       return rel;
+    }
+  return NULL;
+}
+
+static bool
+print_got_plt (struct sec_buf *sb, uint64_t vma, struct disassemble_info *info)
+{
+  if (sb->name != NULL)
+    {
+      asection *s = sb->sec;
+      if (s == NULL)
+       {
+         s = bfd_get_section_by_name (info->section->owner, sb->name);
+         sb->sec = s;
+         if (s == NULL)
+           sb->name = NULL;
+       }
+      if (s != NULL
+         && vma >= s->vma
+         && vma < s->vma + s->size)
+       {
+         asymbol *sym = NULL;
+         uint64_t ent = 0;
+         if (info->dynrelcount > 0)
+           {
+             arelent **lo = info->dynrelbuf;
+             arelent **hi = lo + info->dynrelcount;
+             arelent *rel = bsearch_reloc (lo, hi, vma);
+             if (rel != NULL && rel->sym_ptr_ptr != NULL)
+               sym = *rel->sym_ptr_ptr;
+           }
+         if (sym == NULL && (s->flags & SEC_HAS_CONTENTS) != 0)
+           {
+             if (sb->buf == NULL
+                 && !bfd_malloc_and_get_section (s->owner, s, &sb->buf))
+               sb->name = NULL;
+             if (sb->buf != NULL)
+               {
+                 ent = bfd_get_64 (s->owner, sb->buf + (vma - s->vma));
+                 if (ent != 0)
+                   sym = (*info->symbol_at_address_func) (ent, info);
+               }
+           }
+         if (sym != NULL)
+           (*info->fprintf_func) (info->stream, " [%s@%s]",
+                                  bfd_asymbol_name (sym), sb->name + 1);
+         else
+           (*info->fprintf_func) (info->stream, " [%" PRIx64 "@%s]",
+                                  ent, sb->name + 1);
+         return true;
+       }
+    }
+  return false;
+}
+
 /* Print a PowerPC or POWER instruction.  */
 
 static int
@@ -828,7 +905,7 @@ print_insn_powerpc (bfd_vma memaddr,
     }
   if (opcode == NULL && (dialect & PPC_OPCODE_VLE) != 0)
     {
-      opcode = lookup_vle (insn);
+      opcode = lookup_vle (insn, dialect);
       if (opcode != NULL && PPC_OP_SE_VLE (opcode->mask))
        {
          /* The operands will be fetched out of the 16-bit instruction.  */
@@ -839,7 +916,7 @@ print_insn_powerpc (bfd_vma memaddr,
   if (opcode == NULL && insn_length == 4)
     {
       if ((dialect & PPC_OPCODE_SPE2) != 0)
-       opcode = lookup_spe2 (insn);
+       opcode = lookup_spe2 (insn, dialect);
       if (opcode == NULL)
        opcode = lookup_powerpc (insn, dialect & ~PPC_OPCODE_ANY);
       if (opcode == NULL && (dialect & PPC_OPCODE_ANY) != 0)
@@ -848,7 +925,7 @@ print_insn_powerpc (bfd_vma memaddr,
 
   if (opcode != NULL)
     {
-      const unsigned char *opindex;
+      const ppc_opindex_t *opindex;
       const struct powerpc_operand *operand;
       enum {
        need_comma = 0,
@@ -861,7 +938,9 @@ print_insn_powerpc (bfd_vma memaddr,
        need_7spaces = 7,
        need_paren
       } op_separator;
-      bfd_boolean skip_optional;
+      bool skip_optional;
+      bool is_pcrel;
+      uint64_t d34;
       int blanks;
 
       (*info->fprintf_func) (info->stream, "%s", opcode->name);
@@ -872,7 +951,9 @@ print_insn_powerpc (bfd_vma memaddr,
 
       /* Now extract and print the operands.  */
       op_separator = blanks;
-      skip_optional = FALSE;
+      skip_optional = false;
+      is_pcrel = false;
+      d34 = 0;
       for (opindex = opcode->operands; *opindex != 0; opindex++)
        {
          int64_t value;
@@ -886,7 +967,8 @@ print_insn_powerpc (bfd_vma memaddr,
              && (dialect & PPC_OPCODE_RAW) == 0)
            {
              if (!skip_optional)
-               skip_optional = skip_optional_operands (opindex, insn, dialect);
+               skip_optional = skip_optional_operands (opindex, insn,
+                                                       dialect, &is_pcrel);
              if (skip_optional)
                continue;
            }
@@ -945,6 +1027,11 @@ print_insn_powerpc (bfd_vma memaddr,
          else
            (*info->fprintf_func) (info->stream, "%" PRId64, value);
 
+         if (operand->shift == 52)
+           is_pcrel = value != 0;
+         else if (operand->bitm == UINT64_C (0x3ffffffff))
+           d34 = value;
+
          if (op_separator == need_paren)
            (*info->fprintf_func) (info->stream, ")");
 
@@ -953,6 +1040,29 @@ print_insn_powerpc (bfd_vma memaddr,
            op_separator = need_paren;
        }
 
+      if (is_pcrel)
+       {
+         d34 += memaddr;
+         (*info->fprintf_func) (info->stream, "\t# %" PRIx64, d34);
+         asymbol *sym = (*info->symbol_at_address_func) (d34, info);
+         if (sym)
+           (*info->fprintf_func) (info->stream, " <%s>",
+                                  bfd_asymbol_name (sym));
+
+         if (info->private_data != NULL
+             && info->section != NULL
+             && info->section->owner != NULL
+             && (bfd_get_file_flags (info->section->owner)
+                 & (EXEC_P | DYNAMIC)) != 0
+             && ((insn & ((-1ULL << 50) | (0x3fULL << 26)))
+                 == ((1ULL << 58) | (1ULL << 52) | (57ULL << 26)) /* pld */))
+           {
+             for (int i = 0; i < 2; i++)
+               if (print_got_plt (private_data (info)->special + i, d34, info))
+                 break;
+           }
+       }
+
       /* We have found and printed an instruction.  */
       return insn_length;
     }