|
|
/* $Id$ */ |
|
|
|
|
|
#include <common.h> |
|
|
|
|
|
#include <linux/ctype.h> |
|
|
#include <bedbug/bedbug.h> |
|
|
#include <bedbug/ppc.h> |
|
|
#include <bedbug/regs.h> |
|
|
#include <bedbug/tables.h> |
|
|
|
|
|
#define Elf32_Word unsigned long |
|
|
|
|
|
/* USE_SOURCE_CODE enables some symbolic debugging functions of this |
|
|
code. This is only useful if the program will have access to the |
|
|
source code for the binary being examined. |
|
|
*/ |
|
|
|
|
|
/* #define USE_SOURCE_CODE 1 */ |
|
|
|
|
|
#ifdef USE_SOURCE_CODE |
|
|
extern int line_info_from_addr __P ((Elf32_Word, char *, char *, int *)); |
|
|
extern struct symreflist *symByAddr; |
|
|
extern char *symbol_name_from_addr __P ((Elf32_Word, int, int *)); |
|
|
#endif /* USE_SOURCE_CODE */ |
|
|
|
|
|
int print_operands __P ((struct ppc_ctx *)); |
|
|
int get_operand_value __P ((struct opcode *, unsigned long, |
|
|
enum OP_FIELD, unsigned long *)); |
|
|
struct opcode *find_opcode __P ((unsigned long)); |
|
|
struct opcode *find_opcode_by_name __P ((char *)); |
|
|
char *spr_name __P ((int)); |
|
|
int spr_value __P ((char *)); |
|
|
char *tbr_name __P ((int)); |
|
|
int tbr_value __P ((char *)); |
|
|
int parse_operand __P ((unsigned long, struct opcode *, |
|
|
struct operand *, char *, int *)); |
|
|
int get_word __P ((char **, char *)); |
|
|
long read_number __P ((char *)); |
|
|
int downstring __P ((char *)); |
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Entry point for the PPC disassembler. |
|
|
* |
|
|
* Arguments: |
|
|
* memaddr The address to start disassembling from. |
|
|
* |
|
|
* virtual If this value is non-zero, then this will be |
|
|
* used as the base address for the output and |
|
|
* symbol lookups. If this value is zero then |
|
|
* memaddr is used as the absolute address. |
|
|
* |
|
|
* num_instr The number of instructions to disassemble. Since |
|
|
* each instruction is 32 bits long, this can be |
|
|
* computed if you know the total size of the region. |
|
|
* |
|
|
* pfunc The address of a function that is called to print |
|
|
* each line of output. The function should take a |
|
|
* single character pointer as its parameters a la puts. |
|
|
* |
|
|
* flags Sets options for the output. This is a |
|
|
* bitwise-inclusive-OR of the following |
|
|
* values. Note that only one of the radix |
|
|
* options may be set. |
|
|
* |
|
|
* F_RADOCTAL - output radix is unsigned base 8. |
|
|
* F_RADUDECIMAL - output radix is unsigned base 10. |
|
|
* F_RADSDECIMAL - output radix is signed base 10. |
|
|
* F_RADHEX - output radix is unsigned base 16. |
|
|
* F_SIMPLE - use simplified mnemonics. |
|
|
* F_SYMBOL - lookup symbols for addresses. |
|
|
* F_INSTR - output raw instruction. |
|
|
* F_LINENO - show line # info if available. |
|
|
* |
|
|
* Returns true if the area was successfully disassembled or false if |
|
|
* a problem was encountered with accessing the memory. |
|
|
*/ |
|
|
|
|
|
int disppc (unsigned char *memaddr, unsigned char *virtual, int num_instr, |
|
|
int (*pfunc) (const char *), unsigned long flags) |
|
|
{ |
|
|
int i; |
|
|
struct ppc_ctx ctx; |
|
|
|
|
|
#ifdef USE_SOURCE_CODE |
|
|
int line_no = 0; |
|
|
int last_line_no = 0; |
|
|
char funcname[128] = { 0 }; |
|
|
char filename[256] = { 0 }; |
|
|
char last_funcname[128] = { 0 }; |
|
|
int symoffset; |
|
|
char *symname; |
|
|
char *cursym = (char *) 0; |
|
|
#endif /* USE_SOURCE_CODE */ |
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
ctx.flags = flags; |
|
|
ctx.virtual = virtual; |
|
|
|
|
|
/* Figure out the output radix before we go any further */ |
|
|
|
|
|
if (ctx.flags & F_RADOCTAL) { |
|
|
/* Unsigned octal output */ |
|
|
strcpy (ctx.radix_fmt, "O%o"); |
|
|
} else if (ctx.flags & F_RADUDECIMAL) { |
|
|
/* Unsigned decimal output */ |
|
|
strcpy (ctx.radix_fmt, "%u"); |
|
|
} else if (ctx.flags & F_RADSDECIMAL) { |
|
|
/* Signed decimal output */ |
|
|
strcpy (ctx.radix_fmt, "%d"); |
|
|
} else { |
|
|
/* Unsigned hex output */ |
|
|
strcpy (ctx.radix_fmt, "0x%x"); |
|
|
} |
|
|
|
|
|
if (ctx.virtual == 0) { |
|
|
ctx.virtual = memaddr; |
|
|
} |
|
|
#ifdef USE_SOURCE_CODE |
|
|
if (ctx.flags & F_SYMBOL) { |
|
|
if (symByAddr == 0) /* no symbols loaded */ |
|
|
ctx.flags &= ~F_SYMBOL; |
|
|
else { |
|
|
cursym = (char *) 0; |
|
|
symoffset = 0; |
|
|
} |
|
|
} |
|
|
#endif /* USE_SOURCE_CODE */ |
|
|
|
|
|
/* format each line as "XXXXXXXX: <symbol> IIIIIIII disassembly" where, |
|
|
XXXXXXXX is the memory address in hex, |
|
|
<symbol> is the symbolic location if F_SYMBOL is set. |
|
|
IIIIIIII is the raw machine code in hex if F_INSTR is set, |
|
|
and disassembly is the disassembled machine code with numbers |
|
|
formatted according to the 'radix' parameter */ |
|
|
|
|
|
for (i = 0; i < num_instr; ++i, memaddr += 4, ctx.virtual += 4) { |
|
|
#ifdef USE_SOURCE_CODE |
|
|
if (ctx.flags & F_LINENO) { |
|
|
if ((line_info_from_addr ((Elf32_Word) ctx.virtual, |
|
|
filename, funcname, &line_no) == true) && |
|
|
((line_no != last_line_no) || |
|
|
(strcmp (last_funcname, funcname) != 0))) { |
|
|
print_source_line (filename, funcname, line_no, pfunc); |
|
|
} |
|
|
last_line_no = line_no; |
|
|
strcpy (last_funcname, funcname); |
|
|
} |
|
|
#endif /* USE_SOURCE_CODE */ |
|
|
|
|
|
sprintf (ctx.data, "%08lx: ", (unsigned long) ctx.virtual); |
|
|
ctx.datalen = 10; |
|
|
|
|
|
#ifdef USE_SOURCE_CODE |
|
|
if (ctx.flags & F_SYMBOL) { |
|
|
if ((symname = |
|
|
symbol_name_from_addr((Elf32_Word) ctx.virtual, |
|
|
true, 0)) != 0) { |
|
|
cursym = symname; |
|
|
symoffset = 0; |
|
|
} else { |
|
|
if ((cursym == 0) && |
|
|
((symname = |
|
|
symbol_name_from_addr((Elf32_Word) ctx.virtual, |
|
|
false, &symoffset)) != 0)) { |
|
|
cursym = symname; |
|
|
} else { |
|
|
symoffset += 4; |
|
|
} |
|
|
} |
|
|
|
|
|
if (cursym != 0) { |
|
|
sprintf (&ctx.data[ctx.datalen], "<%s+", cursym); |
|
|
ctx.datalen = strlen (ctx.data); |
|
|
sprintf (&ctx.data[ctx.datalen], ctx.radix_fmt, symoffset); |
|
|
strcat (ctx.data, ">"); |
|
|
ctx.datalen = strlen (ctx.data); |
|
|
} |
|
|
} |
|
|
#endif /* USE_SOURCE_CODE */ |
|
|
|
|
|
ctx.instr = INSTRUCTION (memaddr); |
|
|
|
|
|
if (ctx.flags & F_INSTR) { |
|
|
/* Find the opcode structure for this opcode. If one is not found |
|
|
then it must be an illegal instruction */ |
|
|
sprintf (&ctx.data[ctx.datalen], |
|
|
" %02lx %02lx %02lx %02lx ", |
|
|
((ctx.instr >> 24) & 0xff), |
|
|
((ctx.instr >> 16) & 0xff), ((ctx.instr >> 8) & 0xff), |
|
|
(ctx.instr & 0xff)); |
|
|
ctx.datalen += 18; |
|
|
} else { |
|
|
strcat (ctx.data, " "); |
|
|
ctx.datalen += 3; |
|
|
} |
|
|
|
|
|
if ((ctx.op = find_opcode (ctx.instr)) == 0) { |
|
|
/* Illegal Opcode */ |
|
|
sprintf (&ctx.data[ctx.datalen], " .long 0x%08lx", |
|
|
ctx.instr); |
|
|
ctx.datalen += 24; |
|
|
(*pfunc) (ctx.data); |
|
|
continue; |
|
|
} |
|
|
|
|
|
if (((ctx.flags & F_SIMPLE) == 0) || |
|
|
(ctx.op->hfunc == 0) || |
|
|
((*ctx.op->hfunc) (&ctx) == false)) { |
|
|
sprintf (&ctx.data[ctx.datalen], "%-7s ", ctx.op->name); |
|
|
ctx.datalen += 8; |
|
|
print_operands (&ctx); |
|
|
} |
|
|
|
|
|
(*pfunc) (ctx.data); |
|
|
} |
|
|
|
|
|
return true; |
|
|
} /* disppc */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Called by the disassembler to print the operands for an instruction. |
|
|
* |
|
|
* Arguments: |
|
|
* ctx A pointer to the disassembler context record. |
|
|
* |
|
|
* always returns 0. |
|
|
*/ |
|
|
|
|
|
int print_operands (struct ppc_ctx *ctx) |
|
|
{ |
|
|
int open_parens = 0; |
|
|
int field; |
|
|
unsigned long operand; |
|
|
struct operand *opr; |
|
|
|
|
|
#ifdef USE_SOURCE_CODE |
|
|
char *symname; |
|
|
int offset; |
|
|
#endif /* USE_SOURCE_CODE */ |
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
/* Walk through the operands and list each in order */ |
|
|
for (field = 0; ctx->op->fields[field] != 0; ++field) { |
|
|
if (ctx->op->fields[field] > n_operands) { |
|
|
continue; /* bad operand ?! */ |
|
|
} |
|
|
|
|
|
opr = &operands[ctx->op->fields[field] - 1]; |
|
|
|
|
|
if (opr->hint & OH_SILENT) { |
|
|
continue; |
|
|
} |
|
|
|
|
|
if ((field > 0) && !open_parens) { |
|
|
strcat (ctx->data, ","); |
|
|
ctx->datalen++; |
|
|
} |
|
|
|
|
|
operand = (ctx->instr >> opr->shift) & ((1 << opr->bits) - 1); |
|
|
|
|
|
if (opr->hint & OH_ADDR) { |
|
|
if ((operand & (1 << (opr->bits - 1))) != 0) { |
|
|
operand = operand - (1 << opr->bits); |
|
|
} |
|
|
|
|
|
if (ctx->op->hint & H_RELATIVE) |
|
|
operand = (operand << 2) + (unsigned long) ctx->virtual; |
|
|
else |
|
|
operand = (operand << 2); |
|
|
|
|
|
|
|
|
sprintf (&ctx->data[ctx->datalen], "0x%lx", operand); |
|
|
ctx->datalen = strlen (ctx->data); |
|
|
|
|
|
#ifdef USE_SOURCE_CODE |
|
|
if ((ctx->flags & F_SYMBOL) && |
|
|
((symname = |
|
|
symbol_name_from_addr (operand, 0, &offset)) != 0)) { |
|
|
sprintf (&ctx->data[ctx->datalen], " <%s", symname); |
|
|
if (offset != 0) { |
|
|
strcat (ctx->data, "+"); |
|
|
ctx->datalen = strlen (ctx->data); |
|
|
sprintf (&ctx->data[ctx->datalen], ctx->radix_fmt, |
|
|
offset); |
|
|
} |
|
|
strcat (ctx->data, ">"); |
|
|
} |
|
|
#endif /* USE_SOURCE_CODE */ |
|
|
} |
|
|
|
|
|
else if (opr->hint & OH_REG) { |
|
|
if ((operand == 0) && |
|
|
(opr->field == O_rA) && (ctx->op->hint & H_RA0_IS_0)) { |
|
|
strcat (ctx->data, "0"); |
|
|
} else { |
|
|
sprintf (&ctx->data[ctx->datalen], "r%d", (short) operand); |
|
|
} |
|
|
|
|
|
if (open_parens) { |
|
|
strcat (ctx->data, ")"); |
|
|
open_parens--; |
|
|
} |
|
|
} |
|
|
|
|
|
else if (opr->hint & OH_SPR) { |
|
|
strcat (ctx->data, spr_name (operand)); |
|
|
} |
|
|
|
|
|
else if (opr->hint & OH_TBR) { |
|
|
strcat (ctx->data, tbr_name (operand)); |
|
|
} |
|
|
|
|
|
else if (opr->hint & OH_LITERAL) { |
|
|
switch (opr->field) { |
|
|
case O_cr2: |
|
|
strcat (ctx->data, "cr2"); |
|
|
ctx->datalen += 3; |
|
|
break; |
|
|
|
|
|
default: |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
else { |
|
|
sprintf (&ctx->data[ctx->datalen], ctx->radix_fmt, |
|
|
(unsigned short) operand); |
|
|
|
|
|
if (open_parens) { |
|
|
strcat (ctx->data, ")"); |
|
|
open_parens--; |
|
|
} |
|
|
|
|
|
else if (opr->hint & OH_OFFSET) { |
|
|
strcat (ctx->data, "("); |
|
|
open_parens++; |
|
|
} |
|
|
} |
|
|
|
|
|
ctx->datalen = strlen (ctx->data); |
|
|
} |
|
|
|
|
|
return 0; |
|
|
} /* print_operands */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Called to get the value of an arbitrary operand with in an instruction. |
|
|
* |
|
|
* Arguments: |
|
|
* op The pointer to the opcode structure to which |
|
|
* the operands belong. |
|
|
* |
|
|
* instr The instruction (32 bits) containing the opcode |
|
|
* and the operands to print. By the time that |
|
|
* this routine is called the operand has already |
|
|
* been added to the output. |
|
|
* |
|
|
* field The field (operand) to get the value of. |
|
|
* |
|
|
* value The address of an unsigned long to be filled in |
|
|
* with the value of the operand if it is found. This |
|
|
* will only be filled in if the function returns |
|
|
* true. This may be passed as 0 if the value is |
|
|
* not required. |
|
|
* |
|
|
* Returns true if the operand was found or false if it was not. |
|
|
*/ |
|
|
|
|
|
int get_operand_value (struct opcode *op, unsigned long instr, |
|
|
enum OP_FIELD field, unsigned long *value) |
|
|
{ |
|
|
int i; |
|
|
struct operand *opr; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
if (field > n_operands) { |
|
|
return false; /* bad operand ?! */ |
|
|
} |
|
|
|
|
|
/* Walk through the operands and list each in order */ |
|
|
for (i = 0; op->fields[i] != 0; ++i) { |
|
|
if (op->fields[i] != field) { |
|
|
continue; |
|
|
} |
|
|
|
|
|
opr = &operands[op->fields[i] - 1]; |
|
|
|
|
|
if (value) { |
|
|
*value = (instr >> opr->shift) & ((1 << opr->bits) - 1); |
|
|
} |
|
|
return true; |
|
|
} |
|
|
|
|
|
return false; |
|
|
} /* operand_value */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Called by the disassembler to match an opcode value to an opcode structure. |
|
|
* |
|
|
* Arguments: |
|
|
* instr The instruction (32 bits) to match. This value |
|
|
* may contain operand values as well as the opcode |
|
|
* since they will be masked out anyway for this |
|
|
* search. |
|
|
* |
|
|
* Returns the address of an opcode struct (from the opcode table) if the |
|
|
* operand successfully matched an entry, or 0 if no match was found. |
|
|
*/ |
|
|
|
|
|
struct opcode *find_opcode (unsigned long instr) |
|
|
{ |
|
|
struct opcode *ptr; |
|
|
int top = 0; |
|
|
int bottom = n_opcodes - 1; |
|
|
int idx; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
while (top <= bottom) { |
|
|
idx = (top + bottom) >> 1; |
|
|
ptr = &opcodes[idx]; |
|
|
|
|
|
if ((instr & ptr->mask) < ptr->opcode) { |
|
|
bottom = idx - 1; |
|
|
} else if ((instr & ptr->mask) > ptr->opcode) { |
|
|
top = idx + 1; |
|
|
} else { |
|
|
return ptr; |
|
|
} |
|
|
} |
|
|
|
|
|
return (struct opcode *) 0; |
|
|
} /* find_opcode */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Called by the assembler to match an opcode name to an opcode structure. |
|
|
* |
|
|
* Arguments: |
|
|
* name The text name of the opcode, e.g. "b", "mtspr", etc. |
|
|
* |
|
|
* The opcodes are sorted numerically by their instruction binary code |
|
|
* so a search for the name cannot use the binary search used by the |
|
|
* other find routine. |
|
|
* |
|
|
* Returns the address of an opcode struct (from the opcode table) if the |
|
|
* name successfully matched an entry, or 0 if no match was found. |
|
|
*/ |
|
|
|
|
|
struct opcode *find_opcode_by_name (char *name) |
|
|
{ |
|
|
int idx; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
downstring (name); |
|
|
|
|
|
for (idx = 0; idx < n_opcodes; ++idx) { |
|
|
if (!strcmp (name, opcodes[idx].name)) |
|
|
return &opcodes[idx]; |
|
|
} |
|
|
|
|
|
return (struct opcode *) 0; |
|
|
} /* find_opcode_by_name */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Convert the 'spr' operand from its numeric value to its symbolic name. |
|
|
* |
|
|
* Arguments: |
|
|
* value The value of the 'spr' operand. This value should |
|
|
* be unmodified from its encoding in the instruction. |
|
|
* the split-field computations will be performed |
|
|
* here before the switch. |
|
|
* |
|
|
* Returns the address of a character array containing the name of the |
|
|
* special purpose register defined by the 'value' parameter, or the |
|
|
* address of a character array containing "???" if no match was found. |
|
|
*/ |
|
|
|
|
|
char *spr_name (int value) |
|
|
{ |
|
|
unsigned short spr; |
|
|
static char other[10]; |
|
|
int i; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
/* spr is a 10 bit field whose interpretation has the high and low |
|
|
five-bit fields reversed from their encoding in the operand */ |
|
|
|
|
|
spr = ((value >> 5) & 0x1f) | ((value & 0x1f) << 5); |
|
|
|
|
|
for (i = 0; i < n_sprs; ++i) { |
|
|
if (spr == spr_map[i].spr_val) |
|
|
return spr_map[i].spr_name; |
|
|
} |
|
|
|
|
|
sprintf (other, "%d", spr); |
|
|
return other; |
|
|
} /* spr_name */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Convert the 'spr' operand from its symbolic name to its numeric value |
|
|
* |
|
|
* Arguments: |
|
|
* name The symbolic name of the 'spr' operand. The |
|
|
* split-field encoding will be done by this routine. |
|
|
* NOTE: name can be a number. |
|
|
* |
|
|
* Returns the numeric value for the spr appropriate for encoding a machine |
|
|
* instruction. Returns 0 if unable to find the SPR. |
|
|
*/ |
|
|
|
|
|
int spr_value (char *name) |
|
|
{ |
|
|
struct spr_info *sprp; |
|
|
int spr; |
|
|
int i; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
if (!name || !*name) |
|
|
return 0; |
|
|
|
|
|
if (isdigit ((int) name[0])) { |
|
|
i = htonl (read_number (name)); |
|
|
spr = ((i >> 5) & 0x1f) | ((i & 0x1f) << 5); |
|
|
return spr; |
|
|
} |
|
|
|
|
|
downstring (name); |
|
|
|
|
|
for (i = 0; i < n_sprs; ++i) { |
|
|
sprp = &spr_map[i]; |
|
|
|
|
|
if (strcmp (name, sprp->spr_name) == 0) { |
|
|
/* spr is a 10 bit field whose interpretation has the high and low |
|
|
five-bit fields reversed from their encoding in the operand */ |
|
|
i = htonl (sprp->spr_val); |
|
|
spr = ((i >> 5) & 0x1f) | ((i & 0x1f) << 5); |
|
|
|
|
|
return spr; |
|
|
} |
|
|
} |
|
|
|
|
|
return 0; |
|
|
} /* spr_value */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Convert the 'tbr' operand from its numeric value to its symbolic name. |
|
|
* |
|
|
* Arguments: |
|
|
* value The value of the 'tbr' operand. This value should |
|
|
* be unmodified from its encoding in the instruction. |
|
|
* the split-field computations will be performed |
|
|
* here before the switch. |
|
|
* |
|
|
* Returns the address of a character array containing the name of the |
|
|
* time base register defined by the 'value' parameter, or the address |
|
|
* of a character array containing "???" if no match was found. |
|
|
*/ |
|
|
|
|
|
char *tbr_name (int value) |
|
|
{ |
|
|
unsigned short tbr; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
/* tbr is a 10 bit field whose interpretation has the high and low |
|
|
five-bit fields reversed from their encoding in the operand */ |
|
|
|
|
|
tbr = ((value >> 5) & 0x1f) | ((value & 0x1f) << 5); |
|
|
|
|
|
if (tbr == 268) |
|
|
return "TBL"; |
|
|
|
|
|
else if (tbr == 269) |
|
|
return "TBU"; |
|
|
|
|
|
|
|
|
return "???"; |
|
|
} /* tbr_name */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Convert the 'tbr' operand from its symbolic name to its numeric value. |
|
|
* |
|
|
* Arguments: |
|
|
* name The symbolic name of the 'tbr' operand. The |
|
|
* split-field encoding will be done by this routine. |
|
|
* |
|
|
* Returns the numeric value for the spr appropriate for encoding a machine |
|
|
* instruction. Returns 0 if unable to find the TBR. |
|
|
*/ |
|
|
|
|
|
int tbr_value (char *name) |
|
|
{ |
|
|
int tbr; |
|
|
int val; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
if (!name || !*name) |
|
|
return 0; |
|
|
|
|
|
downstring (name); |
|
|
|
|
|
if (isdigit ((int) name[0])) { |
|
|
val = read_number (name); |
|
|
|
|
|
if (val != 268 && val != 269) |
|
|
return 0; |
|
|
} else if (strcmp (name, "tbl") == 0) |
|
|
val = 268; |
|
|
else if (strcmp (name, "tbu") == 0) |
|
|
val = 269; |
|
|
else |
|
|
return 0; |
|
|
|
|
|
/* tbr is a 10 bit field whose interpretation has the high and low |
|
|
five-bit fields reversed from their encoding in the operand */ |
|
|
|
|
|
val = htonl (val); |
|
|
tbr = ((val >> 5) & 0x1f) | ((val & 0x1f) << 5); |
|
|
return tbr; |
|
|
} /* tbr_name */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* The next several functions (handle_xxx) are the routines that handle |
|
|
* disassembling the opcodes with simplified mnemonics. |
|
|
* |
|
|
* Arguments: |
|
|
* ctx A pointer to the disassembler context record. |
|
|
* |
|
|
* Returns true if the simpler form was printed or false if it was not. |
|
|
*/ |
|
|
|
|
|
int handle_bc (struct ppc_ctx *ctx) |
|
|
{ |
|
|
unsigned long bo; |
|
|
unsigned long bi; |
|
|
static struct opcode blt = { B_OPCODE (16, 0, 0), B_MASK, {O_BD, 0}, |
|
|
0, "blt", H_RELATIVE |
|
|
}; |
|
|
static struct opcode bne = |
|
|
{ B_OPCODE (16, 0, 0), B_MASK, {O_cr2, O_BD, 0}, |
|
|
0, "bne", H_RELATIVE |
|
|
}; |
|
|
static struct opcode bdnz = { B_OPCODE (16, 0, 0), B_MASK, {O_BD, 0}, |
|
|
0, "bdnz", H_RELATIVE |
|
|
}; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
if (get_operand_value(ctx->op, ctx->instr, O_BO, &bo) == false) |
|
|
return false; |
|
|
|
|
|
if (get_operand_value(ctx->op, ctx->instr, O_BI, &bi) == false) |
|
|
return false; |
|
|
|
|
|
if ((bo == 12) && (bi == 0)) { |
|
|
ctx->op = &blt; |
|
|
sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); |
|
|
ctx->datalen += 8; |
|
|
print_operands (ctx); |
|
|
return true; |
|
|
} else if ((bo == 4) && (bi == 10)) { |
|
|
ctx->op = =⃥ |
|
|
sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); |
|
|
ctx->datalen += 8; |
|
|
print_operands (ctx); |
|
|
return true; |
|
|
} else if ((bo == 16) && (bi == 0)) { |
|
|
ctx->op = &bdnz; |
|
|
sprintf (&ctx->data[ctx->datalen], "%-7s ", ctx->op->name); |
|
|
ctx->datalen += 8; |
|
|
print_operands (ctx); |
|
|
return true; |
|
|
} |
|
|
|
|
|
return false; |
|
|
} /* handle_blt */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Outputs source line information for the disassembler. This should |
|
|
* be modified in the future to lookup the actual line of source code |
|
|
* from the file, but for now this will do. |
|
|
* |
|
|
* Arguments: |
|
|
* filename The address of a character array containing the |
|
|
* absolute path and file name of the source file. |
|
|
* |
|
|
* funcname The address of a character array containing the |
|
|
* name of the function (not C++ demangled (yet)) |
|
|
* to which this code belongs. |
|
|
* |
|
|
* line_no An integer specifying the source line number that |
|
|
* generated this code. |
|
|
* |
|
|
* pfunc The address of a function to call to print the output. |
|
|
* |
|
|
* |
|
|
* Returns true if it was able to output the line info, or false if it was |
|
|
* not. |
|
|
*/ |
|
|
|
|
|
int print_source_line (char *filename, char *funcname, |
|
|
int line_no, int (*pfunc) (const char *)) |
|
|
{ |
|
|
char out_buf[256]; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
(*pfunc) (""); /* output a newline */ |
|
|
sprintf (out_buf, "%s %s(): line %d", filename, funcname, line_no); |
|
|
(*pfunc) (out_buf); |
|
|
|
|
|
return true; |
|
|
} /* print_source_line */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Entry point for the PPC assembler. |
|
|
* |
|
|
* Arguments: |
|
|
* asm_buf An array of characters containing the assembly opcode |
|
|
* and operands to convert to a POWERPC machine |
|
|
* instruction. |
|
|
* |
|
|
* Returns the machine instruction or zero. |
|
|
*/ |
|
|
|
|
|
unsigned long asmppc (unsigned long memaddr, char *asm_buf, int *err) |
|
|
{ |
|
|
struct opcode *opc; |
|
|
struct operand *oper[MAX_OPERANDS]; |
|
|
unsigned long instr; |
|
|
unsigned long param; |
|
|
char *ptr = asm_buf; |
|
|
char scratch[20]; |
|
|
int i; |
|
|
int w_operands = 0; /* wanted # of operands */ |
|
|
int n_operands = 0; /* # of operands read */ |
|
|
int asm_debug = 0; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
if (err) |
|
|
*err = 0; |
|
|
|
|
|
if (get_word (&ptr, scratch) == 0) |
|
|
return 0; |
|
|
|
|
|
/* Lookup the opcode structure based on the opcode name */ |
|
|
if ((opc = find_opcode_by_name (scratch)) == (struct opcode *) 0) { |
|
|
if (err) |
|
|
*err = E_ASM_BAD_OPCODE; |
|
|
return 0; |
|
|
} |
|
|
|
|
|
if (asm_debug) { |
|
|
printf ("asmppc: Opcode = \"%s\"\n", opc->name); |
|
|
} |
|
|
|
|
|
for (i = 0; i < 8; ++i) { |
|
|
if (opc->fields[i] == 0) |
|
|
break; |
|
|
++w_operands; |
|
|
} |
|
|
|
|
|
if (asm_debug) { |
|
|
printf ("asmppc: Expecting %d operands\n", w_operands); |
|
|
} |
|
|
|
|
|
instr = opc->opcode; |
|
|
|
|
|
/* read each operand */ |
|
|
while (n_operands < w_operands) { |
|
|
|
|
|
oper[n_operands] = &operands[opc->fields[n_operands] - 1]; |
|
|
|
|
|
if (oper[n_operands]->hint & OH_SILENT) { |
|
|
/* Skip silent operands, they are covered in opc->opcode */ |
|
|
|
|
|
if (asm_debug) { |
|
|
printf ("asmppc: Operand %d \"%s\" SILENT\n", n_operands, |
|
|
oper[n_operands]->name); |
|
|
} |
|
|
|
|
|
++n_operands; |
|
|
continue; |
|
|
} |
|
|
|
|
|
if (get_word (&ptr, scratch) == 0) |
|
|
break; |
|
|
|
|
|
if (asm_debug) { |
|
|
printf ("asmppc: Operand %d \"%s\" : \"%s\"\n", n_operands, |
|
|
oper[n_operands]->name, scratch); |
|
|
} |
|
|
|
|
|
if ((param = parse_operand (memaddr, opc, oper[n_operands], |
|
|
scratch, err)) == -1) |
|
|
return 0; |
|
|
|
|
|
instr |= param; |
|
|
++n_operands; |
|
|
} |
|
|
|
|
|
if (n_operands < w_operands) { |
|
|
if (err) |
|
|
*err = E_ASM_NUM_OPERANDS; |
|
|
return 0; |
|
|
} |
|
|
|
|
|
if (asm_debug) { |
|
|
printf ("asmppc: Instruction = 0x%08lx\n", instr); |
|
|
} |
|
|
|
|
|
return instr; |
|
|
} /* asmppc */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Called by the assembler to interpret a single operand |
|
|
* |
|
|
* Arguments: |
|
|
* ctx A pointer to the disassembler context record. |
|
|
* |
|
|
* Returns 0 if the operand is ok, or -1 if it is bad. |
|
|
*/ |
|
|
|
|
|
int parse_operand (unsigned long memaddr, struct opcode *opc, |
|
|
struct operand *oper, char *txt, int *err) |
|
|
{ |
|
|
long data; |
|
|
long mask; |
|
|
int is_neg = 0; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
mask = (1 << oper->bits) - 1; |
|
|
|
|
|
if (oper->hint & OH_ADDR) { |
|
|
data = read_number (txt); |
|
|
|
|
|
if (opc->hint & H_RELATIVE) |
|
|
data = data - memaddr; |
|
|
|
|
|
if (data < 0) |
|
|
is_neg = 1; |
|
|
|
|
|
data >>= 2; |
|
|
data &= (mask >> 1); |
|
|
|
|
|
if (is_neg) |
|
|
data |= 1 << (oper->bits - 1); |
|
|
} |
|
|
|
|
|
else if (oper->hint & OH_REG) { |
|
|
if (txt[0] == 'r' || txt[0] == 'R') |
|
|
txt++; |
|
|
else if (txt[0] == '%' && (txt[1] == 'r' || txt[1] == 'R')) |
|
|
txt += 2; |
|
|
|
|
|
data = read_number (txt); |
|
|
if (data > 31) { |
|
|
if (err) |
|
|
*err = E_ASM_BAD_REGISTER; |
|
|
return -1; |
|
|
} |
|
|
|
|
|
data = htonl (data); |
|
|
} |
|
|
|
|
|
else if (oper->hint & OH_SPR) { |
|
|
if ((data = spr_value (txt)) == 0) { |
|
|
if (err) |
|
|
*err = E_ASM_BAD_SPR; |
|
|
return -1; |
|
|
} |
|
|
} |
|
|
|
|
|
else if (oper->hint & OH_TBR) { |
|
|
if ((data = tbr_value (txt)) == 0) { |
|
|
if (err) |
|
|
*err = E_ASM_BAD_TBR; |
|
|
return -1; |
|
|
} |
|
|
} |
|
|
|
|
|
else { |
|
|
data = htonl (read_number (txt)); |
|
|
} |
|
|
|
|
|
return (data & mask) << oper->shift; |
|
|
} /* parse_operand */ |
|
|
|
|
|
|
|
|
char *asm_error_str (int err) |
|
|
{ |
|
|
switch (err) { |
|
|
case E_ASM_BAD_OPCODE: |
|
|
return "Bad opcode"; |
|
|
case E_ASM_NUM_OPERANDS: |
|
|
return "Bad number of operands"; |
|
|
case E_ASM_BAD_REGISTER: |
|
|
return "Bad register number"; |
|
|
case E_ASM_BAD_SPR: |
|
|
return "Bad SPR name or number"; |
|
|
case E_ASM_BAD_TBR: |
|
|
return "Bad TBR name or number"; |
|
|
} |
|
|
|
|
|
return ""; |
|
|
} /* asm_error_str */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Copy a word from one buffer to another, ignores leading white spaces. |
|
|
* |
|
|
* Arguments: |
|
|
* src The address of a character pointer to the |
|
|
* source buffer. |
|
|
* dest A pointer to a character buffer to write the word |
|
|
* into. |
|
|
* |
|
|
* Returns the number of non-white space characters copied, or zero. |
|
|
*/ |
|
|
|
|
|
int get_word (char **src, char *dest) |
|
|
{ |
|
|
char *ptr = *src; |
|
|
int nchars = 0; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
/* Eat white spaces */ |
|
|
while (*ptr && isblank (*ptr)) |
|
|
ptr++; |
|
|
|
|
|
if (*ptr == 0) { |
|
|
*src = ptr; |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* Find the text of the word */ |
|
|
while (*ptr && !isblank (*ptr) && (*ptr != ',')) |
|
|
dest[nchars++] = *ptr++; |
|
|
ptr = (*ptr == ',') ? ptr + 1 : ptr; |
|
|
dest[nchars] = 0; |
|
|
|
|
|
*src = ptr; |
|
|
return nchars; |
|
|
} /* get_word */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Convert a numeric string to a number, be aware of base notations. |
|
|
* |
|
|
* Arguments: |
|
|
* txt The numeric string. |
|
|
* |
|
|
* Returns the converted numeric value. |
|
|
*/ |
|
|
|
|
|
long read_number (char *txt) |
|
|
{ |
|
|
long val; |
|
|
int is_neg = 0; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
if (txt == 0 || *txt == 0) |
|
|
return 0; |
|
|
|
|
|
if (*txt == '-') { |
|
|
is_neg = 1; |
|
|
++txt; |
|
|
} |
|
|
|
|
|
if (txt[0] == '0' && (txt[1] == 'x' || txt[1] == 'X')) /* hex */ |
|
|
val = simple_strtoul (&txt[2], NULL, 16); |
|
|
else /* decimal */ |
|
|
val = simple_strtoul (txt, NULL, 10); |
|
|
|
|
|
if (is_neg) |
|
|
val = -val; |
|
|
|
|
|
return val; |
|
|
} /* read_number */ |
|
|
|
|
|
|
|
|
int downstring (char *s) |
|
|
{ |
|
|
if (!s || !*s) |
|
|
return 0; |
|
|
|
|
|
while (*s) { |
|
|
if (isupper (*s)) |
|
|
*s = tolower (*s); |
|
|
s++; |
|
|
} |
|
|
|
|
|
return 0; |
|
|
} /* downstring */ |
|
|
|
|
|
|
|
|
|
|
|
/*====================================================================== |
|
|
* Examines the instruction at the current address and determines the |
|
|
* next address to be executed. This will take into account branches |
|
|
* of different types so that a "step" and "next" operations can be |
|
|
* supported. |
|
|
* |
|
|
* Arguments: |
|
|
* nextaddr The address (to be filled in) of the next |
|
|
* instruction to execute. This will only be a valid |
|
|
* address if true is returned. |
|
|
* |
|
|
* step_over A flag indicating how to compute addresses for |
|
|
* branch statements: |
|
|
* true = Step over the branch (next) |
|
|
* false = step into the branch (step) |
|
|
* |
|
|
* Returns true if it was able to compute the address. Returns false if |
|
|
* it has a problem reading the current instruction or one of the registers. |
|
|
*/ |
|
|
|
|
|
int find_next_address (unsigned char *nextaddr, int step_over, |
|
|
struct pt_regs *regs) |
|
|
{ |
|
|
unsigned long pc; /* SRR0 register from PPC */ |
|
|
unsigned long ctr; /* CTR register from PPC */ |
|
|
unsigned long cr; /* CR register from PPC */ |
|
|
unsigned long lr; /* LR register from PPC */ |
|
|
unsigned long instr; /* instruction at SRR0 */ |
|
|
unsigned long next; /* computed instruction for 'next' */ |
|
|
unsigned long step; /* computed instruction for 'step' */ |
|
|
unsigned long addr = 0; /* target address operand */ |
|
|
unsigned long aa = 0; /* AA operand */ |
|
|
unsigned long lk = 0; /* LK operand */ |
|
|
unsigned long bo = 0; /* BO operand */ |
|
|
unsigned long bi = 0; /* BI operand */ |
|
|
struct opcode *op = 0; /* opcode structure for 'instr' */ |
|
|
int ctr_ok = 0; |
|
|
int cond_ok = 0; |
|
|
int conditional = 0; |
|
|
int branch = 0; |
|
|
|
|
|
/*------------------------------------------------------------*/ |
|
|
|
|
|
if (nextaddr == 0 || regs == 0) { |
|
|
printf ("find_next_address: bad args"); |
|
|
return false; |
|
|
} |
|
|
|
|
|
pc = regs->nip & 0xfffffffc; |
|
|
instr = INSTRUCTION (pc); |
|
|
|
|
|
if ((op = find_opcode (instr)) == (struct opcode *) 0) { |
|
|
printf ("find_next_address: can't parse opcode 0x%lx", instr); |
|
|
return false; |
|
|
} |
|
|
|
|
|
ctr = regs->ctr; |
|
|
cr = regs->ccr; |
|
|
lr = regs->link; |
|
|
|
|
|
switch (op->opcode) { |
|
|
case B_OPCODE (16, 0, 0): /* bc */ |
|
|
case B_OPCODE (16, 0, 1): /* bcl */ |
|
|
case B_OPCODE (16, 1, 0): /* bca */ |
|
|
case B_OPCODE (16, 1, 1): /* bcla */ |
|
|
if (!get_operand_value (op, instr, O_BD, &addr) || |
|
|
!get_operand_value (op, instr, O_BO, &bo) || |
|
|
!get_operand_value (op, instr, O_BI, &bi) || |
|
|
!get_operand_value (op, instr, O_AA, &aa) || |
|
|
!get_operand_value (op, instr, O_LK, &lk)) |
|
|
return false; |
|
|
|
|
|
if ((addr & (1 << 13)) != 0) |
|
|
addr = addr - (1 << 14); |
|
|
addr <<= 2; |
|
|
conditional = 1; |
|
|
branch = 1; |
|
|
break; |
|
|
|
|
|
case I_OPCODE (18, 0, 0): /* b */ |
|
|
case I_OPCODE (18, 0, 1): /* bl */ |
|
|
case I_OPCODE (18, 1, 0): /* ba */ |
|
|
case I_OPCODE (18, 1, 1): /* bla */ |
|
|
if (!get_operand_value (op, instr, O_LI, &addr) || |
|
|
!get_operand_value (op, instr, O_AA, &aa) || |
|
|
!get_operand_value (op, instr, O_LK, &lk)) |
|
|
return false; |
|
|
|
|
|
if ((addr & (1 << 23)) != 0) |
|
|
addr = addr - (1 << 24); |
|
|
addr <<= 2; |
|
|
conditional = 0; |
|
|
branch = 1; |
|
|
break; |
|
|
|
|
|
case XL_OPCODE (19, 528, 0): /* bcctr */ |
|
|
case XL_OPCODE (19, 528, 1): /* bcctrl */ |
|
|
if (!get_operand_value (op, instr, O_BO, &bo) || |
|
|
!get_operand_value (op, instr, O_BI, &bi) || |
|
|
!get_operand_value (op, instr, O_LK, &lk)) |
|
|
return false; |
|
|
|
|
|
addr = ctr; |
|
|
aa = 1; |
|
|
conditional = 1; |
|
|
branch = 1; |
|
|
break; |
|
|
|
|
|
case XL_OPCODE (19, 16, 0): /* bclr */ |
|
|
case XL_OPCODE (19, 16, 1): /* bclrl */ |
|
|
if (!get_operand_value (op, instr, O_BO, &bo) || |
|
|
!get_operand_value (op, instr, O_BI, &bi) || |
|
|
!get_operand_value (op, instr, O_LK, &lk)) |
|
|
return false; |
|
|
|
|
|
addr = lr; |
|
|
aa = 1; |
|
|
conditional = 1; |
|
|
branch = 1; |
|
|
break; |
|
|
|
|
|
default: |
|
|
conditional = 0; |
|
|
branch = 0; |
|
|
break; |
|
|
} |
|
|
|
|
|
if (conditional) { |
|
|
switch ((bo & 0x1e) >> 1) { |
|
|
case 0: /* 0000y */ |
|
|
if (--ctr != 0) |
|
|
ctr_ok = 1; |
|
|
|
|
|
cond_ok = !(cr & (1 << (31 - bi))); |
|
|
break; |
|
|
|
|
|
case 1: /* 0001y */ |
|
|
if (--ctr == 0) |
|
|
ctr_ok = 1; |
|
|
|
|
|
cond_ok = !(cr & (1 << (31 - bi))); |
|
|
break; |
|
|
|
|
|
case 2: /* 001zy */ |
|
|
ctr_ok = 1; |
|
|
cond_ok = !(cr & (1 << (31 - bi))); |
|
|
break; |
|
|
|
|
|
case 4: /* 0100y */ |
|
|
if (--ctr != 0) |
|
|
ctr_ok = 1; |
|
|
|
|
|
cond_ok = cr & (1 << (31 - bi)); |
|
|
break; |
|
|
|
|
|
case 5: /* 0101y */ |
|
|
if (--ctr == 0) |
|
|
ctr_ok = 1; |
|
|
|
|
|
cond_ok = cr & (1 << (31 - bi)); |
|
|
break; |
|
|
|
|
|
case 6: /* 011zy */ |
|
|
ctr_ok = 1; |
|
|
cond_ok = cr & (1 << (31 - bi)); |
|
|
break; |
|
|
|
|
|
case 8: /* 1z00y */ |
|
|
if (--ctr != 0) |
|
|
ctr_ok = cond_ok = 1; |
|
|
break; |
|
|
|
|
|
case 9: /* 1z01y */ |
|
|
if (--ctr == 0) |
|
|
ctr_ok = cond_ok = 1; |
|
|
break; |
|
|
|
|
|
case 10: /* 1z1zz */ |
|
|
ctr_ok = cond_ok = 1; |
|
|
break; |
|
|
} |
|
|
} |
|
|
|
|
|
if (branch && (!conditional || (ctr_ok && cond_ok))) { |
|
|
if (aa) |
|
|
step = addr; |
|
|
else |
|
|
step = addr + pc; |
|
|
|
|
|
if (lk) |
|
|
next = pc + 4; |
|
|
else |
|
|
next = step; |
|
|
} else { |
|
|
step = next = pc + 4; |
|
|
} |
|
|
|
|
|
if (step_over == true) |
|
|
*(unsigned long *) nextaddr = next; |
|
|
else |
|
|
*(unsigned long *) nextaddr = step; |
|
|
|
|
|
return true; |
|
|
} /* find_next_address */ |
|
|
|
|
|
|
|
|
/* |
|
|
* Copyright (c) 2000 William L. Pitts and W. Gerald Hicks |
|
|
* All rights reserved. |
|
|
* |
|
|
* Redistribution and use in source and binary forms are freely |
|
|
* permitted provided that the above copyright notice and this |
|
|
* paragraph and the following disclaimer are duplicated in all |
|
|
* such forms. |
|
|
* |
|
|
* This software is provided "AS IS" and without any express or |
|
|
* implied warranties, including, without limitation, the implied |
|
|
* warranties of merchantability and fitness for a particular |
|
|
* purpose. |
|
|
*/
|
|
|
|