Index: opcodes/ppc-dis.c |
diff --git a/opcodes/ppc-dis.c b/opcodes/ppc-dis.c |
index e0113923c3c9c982ba7467c4540cfca237de11a4..090574451c54356afbd6e58d936b186343a484b2 100644 |
--- a/opcodes/ppc-dis.c |
+++ b/opcodes/ppc-dis.c |
@@ -1,6 +1,6 @@ |
/* ppc-dis.c -- Disassemble PowerPC instructions |
Copyright 1994, 1995, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, |
- 2008, 2009, 2010 Free Software Foundation, Inc. |
+ 2008, 2009, 2010, 2011, 2012 Free Software Foundation, Inc. |
Written by Ian Lance Taylor, Cygnus Support |
This file is part of the GNU opcodes library. |
@@ -20,9 +20,11 @@ |
Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, |
MA 02110-1301, USA. */ |
-#include <stdio.h> |
#include "sysdep.h" |
+#include <stdio.h> |
#include "dis-asm.h" |
+#include "elf-bfd.h" |
+#include "elf/ppc.h" |
#include "opintl.h" |
#include "opcode/ppc.h" |
@@ -38,7 +40,7 @@ struct dis_private |
{ |
/* Stash the result of parsing disassembler_options here. */ |
ppc_cpu_t dialect; |
-}; |
+} private; |
#define POWERPC_DIALECT(INFO) \ |
(((struct dis_private *) ((INFO)->private_data))->dialect) |
@@ -114,6 +116,18 @@ struct ppc_mopt ppc_opts[] = { |
| PPC_OPCODE_E500MC | PPC_OPCODE_64 | PPC_OPCODE_POWER5 |
| PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7), |
0 }, |
+ { "e5500", (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_ISEL |
+ | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK | PPC_OPCODE_RFMCI |
+ | PPC_OPCODE_E500MC | PPC_OPCODE_64 | PPC_OPCODE_POWER4 |
+ | PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 |
+ | PPC_OPCODE_POWER7), |
+ 0 }, |
+ { "e6500", (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_ISEL |
+ | PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK | PPC_OPCODE_RFMCI |
+ | PPC_OPCODE_E500MC | PPC_OPCODE_64 | PPC_OPCODE_ALTIVEC |
+ | PPC_OPCODE_ALTIVEC2 | PPC_OPCODE_E6500 | PPC_OPCODE_POWER4 |
+ | PPC_OPCODE_POWER5 | PPC_OPCODE_POWER6 | PPC_OPCODE_POWER7), |
+ 0 }, |
{ "e500x2", (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_SPE |
| PPC_OPCODE_ISEL | PPC_OPCODE_EFS | PPC_OPCODE_BRLOCK |
| PPC_OPCODE_PMR | PPC_OPCODE_CACHELCK | PPC_OPCODE_RFMCI |
@@ -169,18 +183,41 @@ struct ppc_mopt ppc_opts[] = { |
{ "titan", (PPC_OPCODE_PPC | PPC_OPCODE_BOOKE | PPC_OPCODE_PMR |
| PPC_OPCODE_RFMCI | PPC_OPCODE_TITAN), |
0 }, |
+ { "vle", (PPC_OPCODE_PPC | PPC_OPCODE_ISEL | PPC_OPCODE_VLE), |
+ PPC_OPCODE_VLE }, |
{ "vsx", (PPC_OPCODE_PPC), |
PPC_OPCODE_VSX }, |
}; |
+/* Switch between Booke and VLE dialects for interlinked dumps. */ |
+static ppc_cpu_t |
+get_powerpc_dialect (struct disassemble_info *info) |
+{ |
+ ppc_cpu_t dialect = 0; |
+ |
+ dialect = POWERPC_DIALECT (info); |
+ |
+ /* Disassemble according to the section headers flags for VLE-mode. */ |
+ if (dialect & PPC_OPCODE_VLE |
+ && info->section->owner != NULL |
+ && bfd_get_flavour (info->section->owner) == bfd_target_elf_flavour |
+ && elf_object_id (info->section->owner) == PPC32_ELF_DATA |
+ && (elf_section_flags (info->section) & SHF_PPC_VLE) != 0) |
+ return dialect; |
+ else |
+ return dialect & ~ PPC_OPCODE_VLE; |
+} |
+ |
/* Handle -m and -M options that set cpu type, and .machine arg. */ |
ppc_cpu_t |
ppc_parse_cpu (ppc_cpu_t ppc_cpu, const char *arg) |
{ |
+ const ppc_cpu_t retain_mask = (PPC_OPCODE_ALTIVEC | PPC_OPCODE_VSX |
+ | PPC_OPCODE_SPE | PPC_OPCODE_ANY |
+ | PPC_OPCODE_VLE | PPC_OPCODE_PMR); |
/* Sticky bits. */ |
- ppc_cpu_t retain_flags = ppc_cpu & (PPC_OPCODE_ALTIVEC | PPC_OPCODE_VSX |
- | PPC_OPCODE_SPE | PPC_OPCODE_ANY); |
+ ppc_cpu_t retain_flags = ppc_cpu & retain_mask; |
unsigned int i; |
for (i = 0; i < sizeof (ppc_opts) / sizeof (ppc_opts[0]); i++) |
@@ -189,8 +226,7 @@ ppc_parse_cpu (ppc_cpu_t ppc_cpu, const char *arg) |
if (ppc_opts[i].sticky) |
{ |
retain_flags |= ppc_opts[i].sticky; |
- if ((ppc_cpu & ~(ppc_cpu_t) (PPC_OPCODE_ALTIVEC | PPC_OPCODE_VSX |
- | PPC_OPCODE_SPE | PPC_OPCODE_ANY)) != 0) |
+ if ((ppc_cpu & ~retain_mask) != 0) |
break; |
} |
ppc_cpu = ppc_opts[i].cpu; |
@@ -205,7 +241,7 @@ ppc_parse_cpu (ppc_cpu_t ppc_cpu, const char *arg) |
/* Determine which set of machines to disassemble for. */ |
-static int |
+static void |
powerpc_init_dialect (struct disassemble_info *info) |
{ |
ppc_cpu_t dialect = 0; |
@@ -213,7 +249,7 @@ powerpc_init_dialect (struct disassemble_info *info) |
struct dis_private *priv = calloc (sizeof (*priv), 1); |
if (priv == NULL) |
- return FALSE; |
+ priv = &private; |
arg = info->disassembler_options; |
while (arg != NULL) |
@@ -244,15 +280,67 @@ powerpc_init_dialect (struct disassemble_info *info) |
dialect |= PPC_OPCODE_64; |
else |
dialect &= ~(ppc_cpu_t) PPC_OPCODE_64; |
- /* Choose a reasonable default. */ |
- dialect |= (PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_601 |
- | PPC_OPCODE_ALTIVEC); |
+ if (info->mach == bfd_mach_ppc_vle) |
+ dialect |= PPC_OPCODE_PPC | PPC_OPCODE_VLE; |
+ else |
+ /* Choose a reasonable default. */ |
+ dialect |= (PPC_OPCODE_PPC | PPC_OPCODE_COMMON | PPC_OPCODE_601 |
+ | PPC_OPCODE_ALTIVEC); |
} |
info->private_data = priv; |
POWERPC_DIALECT(info) = dialect; |
+} |
+ |
+#define PPC_OPCD_SEGS 64 |
+static unsigned short powerpc_opcd_indices[PPC_OPCD_SEGS+1]; |
+#define VLE_OPCD_SEGS 32 |
+static unsigned short vle_opcd_indices[VLE_OPCD_SEGS+1]; |
+ |
+/* Calculate opcode table indices to speed up disassembly, |
+ and init dialect. */ |
+ |
+void |
+disassemble_init_powerpc (struct disassemble_info *info) |
+{ |
+ int i; |
+ unsigned short last; |
+ |
+ i = powerpc_num_opcodes; |
+ while (--i >= 0) |
+ { |
+ unsigned op = PPC_OP (powerpc_opcodes[i].opcode); |
- return TRUE; |
+ powerpc_opcd_indices[op] = i; |
+ } |
+ |
+ last = powerpc_num_opcodes; |
+ for (i = PPC_OPCD_SEGS; i > 0; --i) |
+ { |
+ if (powerpc_opcd_indices[i] == 0) |
+ powerpc_opcd_indices[i] = last; |
+ last = powerpc_opcd_indices[i]; |
+ } |
+ |
+ i = vle_num_opcodes; |
+ while (--i >= 0) |
+ { |
+ unsigned op = VLE_OP (vle_opcodes[i].opcode, vle_opcodes[i].mask); |
+ unsigned seg = VLE_OP_TO_SEG (op); |
+ |
+ vle_opcd_indices[seg] = i; |
+ } |
+ |
+ last = vle_num_opcodes; |
+ for (i = VLE_OPCD_SEGS; i > 0; --i) |
+ { |
+ if (vle_opcd_indices[i] == 0) |
+ vle_opcd_indices[i] = last; |
+ last = vle_opcd_indices[i]; |
+ } |
+ |
+ if (info->arch == bfd_arch_powerpc) |
+ powerpc_init_dialect (info); |
} |
/* Print a big endian PowerPC instruction. */ |
@@ -260,9 +348,7 @@ powerpc_init_dialect (struct disassemble_info *info) |
int |
print_insn_big_powerpc (bfd_vma memaddr, struct disassemble_info *info) |
{ |
- if (info->private_data == NULL && !powerpc_init_dialect (info)) |
- return -1; |
- return print_insn_powerpc (memaddr, info, 1, POWERPC_DIALECT(info)); |
+ return print_insn_powerpc (memaddr, info, 1, get_powerpc_dialect (info)); |
} |
/* Print a little endian PowerPC instruction. */ |
@@ -270,9 +356,7 @@ print_insn_big_powerpc (bfd_vma memaddr, struct disassemble_info *info) |
int |
print_insn_little_powerpc (bfd_vma memaddr, struct disassemble_info *info) |
{ |
- if (info->private_data == NULL && !powerpc_init_dialect (info)) |
- return -1; |
- return print_insn_powerpc (memaddr, info, 0, POWERPC_DIALECT(info)); |
+ return print_insn_powerpc (memaddr, info, 0, get_powerpc_dialect (info)); |
} |
/* Print a POWER (RS/6000) instruction. */ |
@@ -296,11 +380,14 @@ operand_value_powerpc (const struct powerpc_operand *operand, |
value = (*operand->extract) (insn, dialect, &invalid); |
else |
{ |
- value = (insn >> operand->shift) & operand->bitm; |
+ if (operand->shift >= 0) |
+ value = (insn >> operand->shift) & operand->bitm; |
+ else |
+ value = (insn << -operand->shift) & operand->bitm; |
if ((operand->flags & PPC_OPERAND_SIGNED) != 0) |
{ |
/* BITM is always some number of zeros followed by some |
- number of ones, followed by some numer of zeros. */ |
+ number of ones, followed by some number of zeros. */ |
unsigned long top = operand->bitm; |
/* top & -top gives the rightmost 1 bit, so this |
fills in any trailing zeros. */ |
@@ -333,6 +420,106 @@ skip_optional_operands (const unsigned char *opindex, |
return 1; |
} |
+/* Find a match for INSN in the opcode table, given machine DIALECT. |
+ A DIALECT of -1 is special, matching all machine opcode variations. */ |
+ |
+static const struct powerpc_opcode * |
+lookup_powerpc (unsigned long insn, ppc_cpu_t dialect) |
+{ |
+ const struct powerpc_opcode *opcode; |
+ const struct powerpc_opcode *opcode_end; |
+ unsigned long op; |
+ |
+ /* Get the major opcode of the instruction. */ |
+ op = PPC_OP (insn); |
+ |
+ /* Find the first match in the opcode table for this major opcode. */ |
+ opcode_end = powerpc_opcodes + powerpc_opcd_indices[op + 1]; |
+ for (opcode = powerpc_opcodes + powerpc_opcd_indices[op]; |
+ opcode < opcode_end; |
+ ++opcode) |
+ { |
+ const unsigned char *opindex; |
+ const struct powerpc_operand *operand; |
+ int invalid; |
+ |
+ if ((insn & opcode->mask) != opcode->opcode |
+ || (dialect != (ppc_cpu_t) -1 |
+ && ((opcode->flags & dialect) == 0 |
+ || (opcode->deprecated & dialect) != 0))) |
+ continue; |
+ |
+ /* Check validity of operands. */ |
+ invalid = 0; |
+ for (opindex = opcode->operands; *opindex != 0; opindex++) |
+ { |
+ operand = powerpc_operands + *opindex; |
+ if (operand->extract) |
+ (*operand->extract) (insn, dialect, &invalid); |
+ } |
+ if (invalid) |
+ continue; |
+ |
+ return opcode; |
+ } |
+ |
+ return NULL; |
+} |
+ |
+/* Find a match for INSN in the VLE opcode table. */ |
+ |
+static const struct powerpc_opcode * |
+lookup_vle (unsigned long insn) |
+{ |
+ const struct powerpc_opcode *opcode; |
+ const struct powerpc_opcode *opcode_end; |
+ unsigned op, seg; |
+ |
+ op = PPC_OP (insn); |
+ if (op >= 0x20 && op <= 0x37) |
+ { |
+ /* This insn has a 4-bit opcode. */ |
+ op &= 0x3c; |
+ } |
+ seg = VLE_OP_TO_SEG (op); |
+ |
+ /* Find the first match in the opcode table for this major opcode. */ |
+ opcode_end = vle_opcodes + vle_opcd_indices[seg + 1]; |
+ for (opcode = vle_opcodes + vle_opcd_indices[seg]; |
+ opcode < opcode_end; |
+ ++opcode) |
+ { |
+ unsigned long table_opcd = opcode->opcode; |
+ unsigned long table_mask = opcode->mask; |
+ bfd_boolean table_op_is_short = PPC_OP_SE_VLE(table_mask); |
+ unsigned long insn2; |
+ const unsigned char *opindex; |
+ const struct powerpc_operand *operand; |
+ int invalid; |
+ |
+ insn2 = insn; |
+ if (table_op_is_short) |
+ insn2 >>= 16; |
+ if ((insn2 & table_mask) != table_opcd) |
+ continue; |
+ |
+ /* Check validity of operands. */ |
+ invalid = 0; |
+ for (opindex = opcode->operands; *opindex != 0; ++opindex) |
+ { |
+ operand = powerpc_operands + *opindex; |
+ if (operand->extract) |
+ (*operand->extract) (insn, (ppc_cpu_t)0, &invalid); |
+ } |
+ if (invalid) |
+ continue; |
+ |
+ return opcode; |
+ } |
+ |
+ return NULL; |
+} |
+ |
/* Print a PowerPC or POWER instruction. */ |
static int |
@@ -345,14 +532,28 @@ print_insn_powerpc (bfd_vma memaddr, |
int status; |
unsigned long insn; |
const struct powerpc_opcode *opcode; |
- const struct powerpc_opcode *opcode_end; |
- unsigned long op; |
+ bfd_boolean insn_is_short; |
status = (*info->read_memory_func) (memaddr, buffer, 4, info); |
if (status != 0) |
{ |
- (*info->memory_error_func) (status, memaddr, info); |
- return -1; |
+ /* The final instruction may be a 2-byte VLE insn. */ |
+ if ((dialect & PPC_OPCODE_VLE) != 0) |
+ { |
+ /* Clear buffer so unused bytes will not have garbage in them. */ |
+ buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0; |
+ status = (*info->read_memory_func) (memaddr, buffer, 2, info); |
+ if (status != 0) |
+ { |
+ (*info->memory_error_func) (status, memaddr, info); |
+ return -1; |
+ } |
+ } |
+ else |
+ { |
+ (*info->memory_error_func) (status, memaddr, info); |
+ return -1; |
+ } |
} |
if (bigendian) |
@@ -360,54 +561,37 @@ print_insn_powerpc (bfd_vma memaddr, |
else |
insn = bfd_getl32 (buffer); |
- /* Get the major opcode of the instruction. */ |
- op = PPC_OP (insn); |
+ /* Get the major opcode of the insn. */ |
+ opcode = NULL; |
+ insn_is_short = FALSE; |
+ if ((dialect & PPC_OPCODE_VLE) != 0) |
+ { |
+ opcode = lookup_vle (insn); |
+ if (opcode != NULL) |
+ insn_is_short = PPC_OP_SE_VLE(opcode->mask); |
+ } |
+ if (opcode == NULL) |
+ opcode = lookup_powerpc (insn, dialect); |
+ if (opcode == NULL && (dialect & PPC_OPCODE_ANY) != 0) |
+ opcode = lookup_powerpc (insn, (ppc_cpu_t) -1); |
- /* Find the first match in the opcode table. We could speed this up |
- a bit by doing a binary search on the major opcode. */ |
- opcode_end = powerpc_opcodes + powerpc_num_opcodes; |
- again: |
- for (opcode = powerpc_opcodes; opcode < opcode_end; opcode++) |
+ if (opcode != NULL) |
{ |
- unsigned long table_op; |
const unsigned char *opindex; |
const struct powerpc_operand *operand; |
- int invalid; |
int need_comma; |
int need_paren; |
int skip_optional; |
- table_op = PPC_OP (opcode->opcode); |
- if (op < table_op) |
- break; |
- if (op > table_op) |
- continue; |
- |
- if ((insn & opcode->mask) != opcode->opcode |
- || (opcode->flags & dialect) == 0 |
- || (dialect != ~(ppc_cpu_t) PPC_OPCODE_ANY |
- && (opcode->deprecated & dialect) != 0)) |
- continue; |
- |
- /* Make two passes over the operands. First see if any of them |
- have extraction functions, and, if they do, make sure the |
- instruction is valid. */ |
- invalid = 0; |
- for (opindex = opcode->operands; *opindex != 0; opindex++) |
- { |
- operand = powerpc_operands + *opindex; |
- if (operand->extract) |
- (*operand->extract) (insn, dialect, &invalid); |
- } |
- if (invalid) |
- continue; |
- |
- /* The instruction is valid. */ |
if (opcode->operands[0] != 0) |
(*info->fprintf_func) (info->stream, "%-7s ", opcode->name); |
else |
(*info->fprintf_func) (info->stream, "%s", opcode->name); |
+ if (insn_is_short) |
+ /* The operands will be fetched out of the 16-bit instruction. */ |
+ insn >>= 16; |
+ |
/* Now extract and print the operands. */ |
need_comma = 0; |
need_paren = 0; |
@@ -463,26 +647,26 @@ print_insn_powerpc (bfd_vma memaddr, |
(*info->fprintf_func) (info->stream, "fcr%ld", value); |
else if ((operand->flags & PPC_OPERAND_UDI) != 0) |
(*info->fprintf_func) (info->stream, "%ld", value); |
- else if ((operand->flags & PPC_OPERAND_CR) != 0 |
- && (dialect & PPC_OPCODE_PPC) != 0) |
+ else if ((operand->flags & PPC_OPERAND_CR_REG) != 0 |
+ && (((dialect & PPC_OPCODE_PPC) != 0) |
+ || ((dialect & PPC_OPCODE_VLE) != 0))) |
+ (*info->fprintf_func) (info->stream, "cr%ld", value); |
+ else if (((operand->flags & PPC_OPERAND_CR_BIT) != 0) |
+ && (((dialect & PPC_OPCODE_PPC) != 0) |
+ || ((dialect & PPC_OPCODE_VLE) != 0))) |
{ |
- if (operand->bitm == 7) |
- (*info->fprintf_func) (info->stream, "cr%ld", value); |
- else |
- { |
- static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; |
- int cr; |
- int cc; |
- |
- cr = value >> 2; |
- if (cr != 0) |
- (*info->fprintf_func) (info->stream, "4*cr%d+", cr); |
- cc = value & 3; |
- (*info->fprintf_func) (info->stream, "%s", cbnames[cc]); |
- } |
+ static const char *cbnames[4] = { "lt", "gt", "eq", "so" }; |
+ int cr; |
+ int cc; |
+ |
+ cr = value >> 2; |
+ if (cr != 0) |
+ (*info->fprintf_func) (info->stream, "4*cr%d+", cr); |
+ cc = value & 3; |
+ (*info->fprintf_func) (info->stream, "%s", cbnames[cc]); |
} |
else |
- (*info->fprintf_func) (info->stream, "%ld", value); |
+ (*info->fprintf_func) (info->stream, "%d", value); |
if (need_paren) |
{ |
@@ -499,14 +683,16 @@ print_insn_powerpc (bfd_vma memaddr, |
} |
} |
- /* We have found and printed an instruction; return. */ |
- return 4; |
- } |
- |
- if ((dialect & PPC_OPCODE_ANY) != 0) |
- { |
- dialect = ~(ppc_cpu_t) PPC_OPCODE_ANY; |
- goto again; |
+ /* We have found and printed an instruction. |
+ If it was a short VLE instruction we have more to do. */ |
+ if (insn_is_short) |
+ { |
+ memaddr += 2; |
+ return 2; |
+ } |
+ else |
+ /* Otherwise, return. */ |
+ return 4; |
} |
/* We could not find a match. */ |