/* ppc-dis.c -- Disassemble PowerPC instructions
- Copyright (C) 1994-2020 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.
{
/* 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. */
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
}
info->private_data = priv;
- POWERPC_DIALECT(info) = dialect;
+ private_data (info)->dialect = dialect;
}
#define PPC_OPCD_SEGS (1 + PPC_OP (-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);
- est = elf_symbol_from (NULL, 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,
}
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. */
/* 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;
{
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. */
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. */
/* 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. */
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. */
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. */
/* 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. */
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;
{
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. */
/* 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;
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. */
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
}
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. */
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)
if (opcode != NULL)
{
- const unsigned char *opindex;
+ const ppc_opindex_t *opindex;
const struct powerpc_operand *operand;
enum {
need_comma = 0,
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);
/* 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;
&& (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;
}
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, ")");
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;
}