568 lines
12 KiB
C
568 lines
12 KiB
C
/* $NetBSD: db_disasm.c,v 1.10 1998/04/13 12:10:27 ragge Exp $ */
|
|
/*
|
|
* Copyright (c) 1996 Ludd, University of Lule}, Sweden.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Ludd by
|
|
* Bertram Barth.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed at Ludd, University of
|
|
* Lule}, Sweden and its contributors.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/reboot.h>
|
|
#include <sys/systm.h>
|
|
|
|
#include <machine/db_machdep.h>
|
|
#include <ddb/db_sym.h>
|
|
#include <ddb/db_variables.h>
|
|
#include <ddb/db_interface.h>
|
|
#include <ddb/db_output.h>
|
|
|
|
#include <vax/vax/db_disasm.h>
|
|
|
|
#ifdef VMS_MODE
|
|
#define DEFERRED '@'
|
|
#define LITERAL '#'
|
|
#else
|
|
#define DEFERRED '*'
|
|
#define LITERAL '$'
|
|
#endif
|
|
/*
|
|
* disassembling vax instructions works as follows:
|
|
*
|
|
* 1. get first byte as opcode (check for two-byte opcodes!)
|
|
* 2. lookup in op-table for mnemonic and operand-list
|
|
* 2.a store the mnemonic
|
|
* 3. for each operand in list: get the size/type
|
|
* 3.a evaluate addressing mode for this operand
|
|
* 3.b store each operand(s)
|
|
* 4. db_printf the opcode and the (value of the) operands
|
|
* 5. return the start of the next instruction
|
|
*
|
|
* - if jump/branch calculate (and display) the target-address
|
|
*/
|
|
|
|
/*
|
|
#define BROKEN_DB_REGS
|
|
*/
|
|
#ifdef BROKEN_DB_REGS
|
|
struct { /* Due to order and contents of db_regs[], we can't */
|
|
char *name; /* use this array to extract register-names. */
|
|
void *valuep; /* eg. "psl" vs "pc", "pc" vs "sp" */
|
|
} my_db_regs[16] = {
|
|
{ "r0", NULL },
|
|
{ "r1", NULL },
|
|
{ "r2", NULL },
|
|
{ "r3", NULL },
|
|
{ "r4", NULL },
|
|
{ "r5", NULL },
|
|
{ "r6", NULL },
|
|
{ "r7", NULL },
|
|
{ "r8", NULL },
|
|
{ "r9", NULL },
|
|
{ "r10", NULL },
|
|
{ "r11", NULL },
|
|
{ "ap", NULL }, /* aka "r12" */
|
|
{ "fp", NULL }, /* aka "r13" */
|
|
{ "sp", NULL }, /* aka "r14" */
|
|
{ "pc", NULL }, /* aka "r15" */
|
|
};
|
|
#else
|
|
#define my_db_regs db_regs
|
|
#endif
|
|
|
|
typedef struct {
|
|
char dasm[256]; /* disassebled instruction as text */
|
|
char *curp; /* pointer into result */
|
|
char *ppc; /* pseudo PC */
|
|
int opc; /* op-code */
|
|
char *argp; /* pointer into argument-list */
|
|
int itype; /* instruction-type, eg. branch, call, unspec */
|
|
int atype; /* argument-type, eg. byte, long, address */
|
|
int off; /* offset specified by last argument */
|
|
int addr; /* address specified by last argument */
|
|
} inst_buffer;
|
|
|
|
#define ITYPE_INVALID -1
|
|
#define ITYPE_UNSPEC 0
|
|
#define ITYPE_BRANCH 1
|
|
#define ITYPE_CALL 2
|
|
|
|
int get_byte __P((inst_buffer * ib));
|
|
int get_word __P((inst_buffer * ib));
|
|
int get_long __P((inst_buffer * ib));
|
|
|
|
int get_opcode __P((inst_buffer * ib));
|
|
int get_operands __P((inst_buffer * ib));
|
|
int get_operand __P((inst_buffer * ib, int size));
|
|
|
|
void add_char __P((inst_buffer * ib, int c));
|
|
void add_str __P((inst_buffer * ib, char *s));
|
|
void add_int __P((inst_buffer * ib, int i));
|
|
void add_xint __P((inst_buffer * ib, int i));
|
|
void add_sym __P((inst_buffer * ib, int i));
|
|
void add_off __P((inst_buffer * ib, int i));
|
|
|
|
#define err_print printf
|
|
|
|
/*
|
|
* Disassemble instruction at 'loc'. 'altfmt' specifies an
|
|
* (optional) alternate format (altfmt for vax: don't assume
|
|
* that each external label is a procedure entry mask).
|
|
* Return address of start of next instruction.
|
|
* Since this function is used by 'examine' and by 'step'
|
|
* "next instruction" does NOT mean the next instruction to
|
|
* be executed but the 'linear' next instruction.
|
|
*/
|
|
db_addr_t
|
|
db_disasm(loc, altfmt)
|
|
db_addr_t loc;
|
|
boolean_t altfmt;
|
|
{
|
|
db_expr_t diff;
|
|
db_sym_t sym;
|
|
char *symname;
|
|
|
|
inst_buffer ib;
|
|
|
|
bzero(&ib, sizeof(ib));
|
|
ib.ppc = (void *) loc;
|
|
ib.curp = ib.dasm;
|
|
|
|
if (!altfmt) { /* ignore potential entry masks in altfmt */
|
|
diff = INT_MAX;
|
|
symname = NULL;
|
|
sym = db_search_symbol(loc, DB_STGY_PROC, &diff);
|
|
db_symbol_values(sym, &symname, 0);
|
|
|
|
if (symname && !diff) { /* symbol at loc */
|
|
db_printf("function \"%s()\", entry-mask 0x%x\n\t\t",
|
|
symname, (unsigned short) get_word(&ib));
|
|
ib.ppc += 2;
|
|
}
|
|
}
|
|
get_opcode(&ib);
|
|
get_operands(&ib);
|
|
db_printf("%s\n", ib.dasm);
|
|
|
|
return ((u_int) ib.ppc);
|
|
}
|
|
|
|
int
|
|
get_opcode(ib)
|
|
inst_buffer *ib;
|
|
{
|
|
ib->opc = get_byte(ib);
|
|
if (ib->opc >> 2 == 0x3F) { /* two byte op-code */
|
|
ib->opc = ib->opc << 8;
|
|
ib->opc += get_byte(ib);
|
|
}
|
|
switch (ib->opc) {
|
|
case 0xFA: /* CALLG */
|
|
case 0xFB: /* CALLS */
|
|
case 0xFC: /* XFC */
|
|
ib->itype = ITYPE_CALL;
|
|
break;
|
|
case 0x16: /* JSB */
|
|
case 0x17: /* JMP */
|
|
ib->itype = ITYPE_BRANCH;
|
|
break;
|
|
default:
|
|
ib->itype = ITYPE_UNSPEC;
|
|
}
|
|
if (ib->opc < 0 || ib->opc > 0xFF) {
|
|
add_str(ib, "invalid or two-byte opcode ");
|
|
add_xint(ib, ib->opc);
|
|
ib->itype = ITYPE_INVALID;
|
|
} else {
|
|
add_str(ib, vax_inst[ib->opc].mnemonic);
|
|
add_char(ib, '\t');
|
|
}
|
|
return (ib->opc);
|
|
}
|
|
|
|
int
|
|
get_operands(ib)
|
|
inst_buffer *ib;
|
|
{
|
|
int aa = 0; /* absolute address mode ? */
|
|
int size;
|
|
|
|
if (ib->opc < 0 || ib->opc > 0xFF) {
|
|
/* invalid or two-byte opcode */
|
|
ib->argp = NULL;
|
|
return (-1);
|
|
}
|
|
ib->argp = vax_inst[ib->opc].argdesc;
|
|
|
|
while (*ib->argp) {
|
|
switch (*ib->argp) {
|
|
|
|
case 'b': /* branch displacement */
|
|
switch (*(++ib->argp)) {
|
|
case 'b':
|
|
ib->off = (signed char) get_byte(ib);
|
|
break;
|
|
case 'w':
|
|
ib->off = (short) get_word(ib);
|
|
break;
|
|
case 'l':
|
|
ib->off = get_long(ib);
|
|
break;
|
|
default:
|
|
err_print("XXX eror\n");
|
|
}
|
|
/* add_int(ib, ib->off); */
|
|
ib->addr = (u_int) ib->ppc + ib->off;
|
|
add_off(ib, ib->addr);
|
|
break;
|
|
|
|
case 'a': /* absolute adressing mode */
|
|
aa = 1; /* do not break here ! */
|
|
|
|
default:
|
|
switch (*(++ib->argp)) {
|
|
case 'b': /* Byte */
|
|
size = SIZE_BYTE;
|
|
break;
|
|
case 'w': /* Word */
|
|
size = SIZE_WORD;
|
|
break;
|
|
case 'l': /* Long-Word */
|
|
case 'f': /* F_Floating */
|
|
size = SIZE_LONG;
|
|
break;
|
|
case 'q': /* Quad-Word */
|
|
case 'd': /* D_Floating */
|
|
case 'g': /* G_Floating */
|
|
size = SIZE_QWORD;
|
|
break;
|
|
case 'o': /* Octa-Word */
|
|
case 'h': /* H_Floating */
|
|
size = SIZE_OWORD;
|
|
break;
|
|
default:
|
|
err_print("invalid op-type %X (%c) found.\n",
|
|
*ib->argp, *ib->argp);
|
|
size = 0;
|
|
}
|
|
if (aa) {
|
|
/* get the address */
|
|
ib->addr = get_operand(ib, size);
|
|
add_sym(ib, ib->addr);
|
|
} else {
|
|
/* get the operand */
|
|
ib->addr = get_operand(ib, size);
|
|
add_off(ib, ib->addr);
|
|
}
|
|
}
|
|
|
|
if (!*ib->argp || !*++ib->argp)
|
|
break;
|
|
if (*ib->argp++ == ',') {
|
|
add_char(ib, ',');
|
|
add_char(ib, ' ');
|
|
} else {
|
|
err_print("XXX error\n");
|
|
add_char(ib, '\0');
|
|
return (-1);
|
|
}
|
|
}
|
|
|
|
add_char(ib, '\0');
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
get_operand(ib, size)
|
|
inst_buffer *ib;
|
|
int size;
|
|
{
|
|
int c = get_byte(ib);
|
|
int mode = c >> 4;
|
|
int reg = c & 0x0F;
|
|
int lit = c & 0x3F;
|
|
int tmp = 0;
|
|
char buf[16];
|
|
|
|
switch (mode) {
|
|
case 0: /* literal */
|
|
case 1: /* literal */
|
|
case 2: /* literal */
|
|
case 3: /* literal */
|
|
add_char(ib, LITERAL);
|
|
add_int(ib, lit);
|
|
tmp = lit;
|
|
break;
|
|
|
|
case 4: /* indexed */
|
|
sprintf(buf, "[%s]", my_db_regs[reg].name);
|
|
get_operand(ib, 0);
|
|
add_str(ib, buf);
|
|
break;
|
|
|
|
case 5: /* register */
|
|
add_str(ib, my_db_regs[reg].name);
|
|
break;
|
|
|
|
case 6: /* register deferred */
|
|
add_char(ib, '(');
|
|
add_str(ib, my_db_regs[reg].name);
|
|
add_char(ib, ')');
|
|
break;
|
|
|
|
case 7: /* autodecrement */
|
|
add_char(ib, '-');
|
|
add_char(ib, '(');
|
|
add_str(ib, my_db_regs[reg].name);
|
|
add_char(ib, ')');
|
|
if (reg == 0x0F) { /* pc is not allowed in this mode */
|
|
err_print("autodecrement not allowd for PC.\n");
|
|
}
|
|
break;
|
|
|
|
case 9: /* autoincrement deferred */
|
|
add_char(ib, DEFERRED);
|
|
if (reg == 0x0F) { /* pc: immediate deferred */
|
|
/*
|
|
* addresses are always longwords!
|
|
*/
|
|
tmp = get_long(ib);
|
|
add_off(ib, tmp);
|
|
break;
|
|
}
|
|
/* fall through */
|
|
case 8: /* autoincrement */
|
|
if (reg == 0x0F) { /* pc: immediate ==> special syntax */
|
|
switch (size) {
|
|
case SIZE_BYTE:
|
|
tmp = (signed char) get_byte(ib);
|
|
break;
|
|
case SIZE_WORD:
|
|
tmp = (signed short) get_word(ib);
|
|
break;
|
|
case SIZE_LONG:
|
|
tmp = get_long(ib);
|
|
break;
|
|
default:
|
|
err_print("illegal op-type %d\n", size);
|
|
tmp = -1;
|
|
}
|
|
if (mode == 8)
|
|
add_char(ib, LITERAL);
|
|
add_int(ib, tmp);
|
|
break;
|
|
}
|
|
add_char(ib, '(');
|
|
add_str(ib, my_db_regs[reg].name);
|
|
add_char(ib, ')');
|
|
add_char(ib, '+');
|
|
break;
|
|
|
|
case 11: /* byte displacement deferred/ relative deferred */
|
|
add_char(ib, DEFERRED);
|
|
case 10: /* byte displacement / relative mode */
|
|
tmp = (signed char) get_byte(ib);
|
|
if (reg == 0x0F) {
|
|
add_off(ib, (u_int) ib->ppc + tmp);
|
|
break;
|
|
}
|
|
/* add_str (ib, "b^"); */
|
|
add_int(ib, tmp);
|
|
add_char(ib, '(');
|
|
add_str(ib, my_db_regs[reg].name);
|
|
add_char(ib, ')');
|
|
break;
|
|
|
|
case 13: /* word displacement deferred */
|
|
add_char(ib, DEFERRED);
|
|
case 12: /* word displacement */
|
|
tmp = (signed short) get_word(ib);
|
|
if (reg == 0x0F) {
|
|
add_off(ib, (u_int) ib->ppc + tmp);
|
|
break;
|
|
}
|
|
/* add_str (ib, "w^"); */
|
|
add_int(ib, tmp);
|
|
add_char(ib, '(');
|
|
add_str(ib, my_db_regs[reg].name);
|
|
add_char(ib, ')');
|
|
break;
|
|
|
|
case 15: /* long displacement referred */
|
|
add_char(ib, DEFERRED);
|
|
case 14: /* long displacement */
|
|
tmp = get_long(ib);
|
|
if (reg == 0x0F) {
|
|
add_off(ib, (u_int) ib->ppc + tmp);
|
|
break;
|
|
}
|
|
/* add_str (ib, "l^"); */
|
|
add_int(ib, tmp);
|
|
add_char(ib, '(');
|
|
add_str(ib, my_db_regs[reg].name);
|
|
add_char(ib, ')');
|
|
break;
|
|
|
|
default:
|
|
err_print("can\'t evaluate operand (%02X).\n", lit);
|
|
break;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
get_byte(ib)
|
|
inst_buffer *ib;
|
|
{
|
|
return ((unsigned char) *(ib->ppc++));
|
|
}
|
|
|
|
int
|
|
get_word(ib)
|
|
inst_buffer *ib;
|
|
{
|
|
int tmp;
|
|
char *p = (void *) &tmp;
|
|
*p++ = get_byte(ib);
|
|
*p++ = get_byte(ib);
|
|
return (tmp);
|
|
}
|
|
|
|
int
|
|
get_long(ib)
|
|
inst_buffer *ib;
|
|
{
|
|
int tmp;
|
|
char *p = (void *) &tmp;
|
|
*p++ = get_byte(ib);
|
|
*p++ = get_byte(ib);
|
|
*p++ = get_byte(ib);
|
|
*p++ = get_byte(ib);
|
|
return (tmp);
|
|
}
|
|
|
|
void
|
|
add_char(ib, c)
|
|
inst_buffer *ib;
|
|
int c;
|
|
{
|
|
*ib->curp++ = c;
|
|
}
|
|
|
|
void
|
|
add_str(ib, s)
|
|
inst_buffer *ib;
|
|
char *s;
|
|
{
|
|
while ((*ib->curp++ = *s++));
|
|
*--ib->curp = '\0';
|
|
}
|
|
|
|
void
|
|
add_int(ib, i)
|
|
inst_buffer *ib;
|
|
int i;
|
|
{
|
|
char buf[32];
|
|
if (i < 100 && i > -100)
|
|
sprintf(buf, "%d", i);
|
|
else
|
|
sprintf(buf, "0x%x", i);
|
|
add_str(ib, buf);
|
|
}
|
|
|
|
void
|
|
add_xint(ib, val)
|
|
inst_buffer *ib;
|
|
int val;
|
|
{
|
|
char buf[32];
|
|
sprintf(buf, "0x%x", val);
|
|
add_str(ib, buf);
|
|
}
|
|
|
|
void
|
|
add_sym(ib, loc)
|
|
inst_buffer *ib;
|
|
int loc;
|
|
{
|
|
db_expr_t diff;
|
|
db_sym_t sym;
|
|
char *symname;
|
|
|
|
if (! loc)
|
|
return;
|
|
|
|
diff = INT_MAX;
|
|
symname = NULL;
|
|
sym = db_search_symbol(loc, DB_STGY_ANY, &diff);
|
|
db_symbol_values(sym, &symname, 0);
|
|
|
|
if (symname && !diff) {
|
|
/* add_char(ib, '<'); */
|
|
add_str(ib, symname);
|
|
/* add_char(ib, '>'); */
|
|
}
|
|
else
|
|
add_xint(ib, loc);
|
|
}
|
|
|
|
void
|
|
add_off(ib, loc)
|
|
inst_buffer *ib;
|
|
int loc;
|
|
{
|
|
db_expr_t diff;
|
|
db_sym_t sym;
|
|
char *symname;
|
|
|
|
if (!loc)
|
|
return;
|
|
|
|
diff = INT_MAX;
|
|
symname = NULL;
|
|
sym = db_search_symbol(loc, DB_STGY_ANY, &diff);
|
|
db_symbol_values(sym, &symname, 0);
|
|
|
|
if (symname) {
|
|
/* add_char(ib, '<'); */
|
|
add_str(ib, symname);
|
|
if (diff) {
|
|
add_char(ib, '+');
|
|
add_xint(ib, diff);
|
|
}
|
|
/* add_char(ib, '>'); */
|
|
}
|
|
else
|
|
add_xint(ib, loc);
|
|
}
|