Esempio n. 1
0
/*
 * Rewrite the xlate/xlarg instruction at dtdo_buf[i] so that the instruction's
 * xltab index reflects the offset 'xi' of the assigned dtdo_xlmtab[] location.
 * We track the cumulative references to translators and members in the pcb's
 * pcb_asxrefs[] array, a two-dimensional array of bitmaps indexed by the
 * global translator id and then by the corresponding translator member id.
 */
static void
dt_as_xlate(dt_pcb_t *pcb, dtrace_difo_t *dp,
    uint_t i, uint_t xi, dt_node_t *dnp)
{
	dtrace_hdl_t *dtp = pcb->pcb_hdl;
	dt_xlator_t *dxp = dnp->dn_membexpr->dn_xlator;

	assert(i < dp->dtdo_len);
	assert(xi < dp->dtdo_xlmlen);

	assert(dnp->dn_kind == DT_NODE_MEMBER);
	assert(dnp->dn_membexpr->dn_kind == DT_NODE_XLATOR);

	assert(dxp->dx_id < dtp->dt_xlatorid);
	assert(dnp->dn_membid < dxp->dx_nmembers);

	if (pcb->pcb_asxrefs == NULL) {
		pcb->pcb_asxreflen = dtp->dt_xlatorid;
		pcb->pcb_asxrefs =
		    dt_zalloc(dtp, sizeof (ulong_t *) * pcb->pcb_asxreflen);
		if (pcb->pcb_asxrefs == NULL)
			longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);
	}

	if (pcb->pcb_asxrefs[dxp->dx_id] == NULL) {
		pcb->pcb_asxrefs[dxp->dx_id] =
		    dt_zalloc(dtp, BT_SIZEOFMAP(dxp->dx_nmembers));
		if (pcb->pcb_asxrefs[dxp->dx_id] == NULL)
			longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);
	}

	dp->dtdo_buf[i] = DIF_INSTR_XLATE(
	    DIF_INSTR_OP(dp->dtdo_buf[i]), xi, DIF_INSTR_RD(dp->dtdo_buf[i]));

	BT_SET(pcb->pcb_asxrefs[dxp->dx_id], dnp->dn_membid);
	dp->dtdo_xlmtab[xi] = dnp;
}
Esempio n. 2
0
dtrace_difo_t *
dt_as(dt_pcb_t *pcb)
{
	dtrace_hdl_t *dtp = pcb->pcb_hdl;
	dt_irlist_t *dlp = &pcb->pcb_ir;
	uint_t *labels = NULL;
	dt_irnode_t *dip;
	dtrace_difo_t *dp;
	dt_ident_t *idp;

	size_t n = 0;
	uint_t i;

	uint_t kmask, kbits, umask, ubits;
	uint_t krel = 0, urel = 0, xlrefs = 0;

	/*
	 * Select bitmasks based upon the desired symbol linking policy.  We
	 * test (di_extern->di_flags & xmask) == xbits to determine if the
	 * symbol should have a relocation entry generated in the loop below.
	 *
	 * DT_LINK_KERNEL = kernel symbols static, user symbols dynamic
	 * DT_LINK_PRIMARY = primary kernel symbols static, others dynamic
	 * DT_LINK_DYNAMIC = all symbols dynamic
	 * DT_LINK_STATIC = all symbols static
	 *
	 * By 'static' we mean that we use the symbol's value at compile-time
	 * in the final DIF.  By 'dynamic' we mean that we create a relocation
	 * table entry for the symbol's value so it can be relocated later.
	 */
	switch (dtp->dt_linkmode) {
	case DT_LINK_KERNEL:
		kmask = 0;
		kbits = -1u;
		umask = DT_IDFLG_USER;
		ubits = DT_IDFLG_USER;
		break;
	case DT_LINK_PRIMARY:
		kmask = DT_IDFLG_USER | DT_IDFLG_PRIM;
		kbits = 0;
		umask = DT_IDFLG_USER;
		ubits = DT_IDFLG_USER;
		break;
	case DT_LINK_DYNAMIC:
		kmask = DT_IDFLG_USER;
		kbits = 0;
		umask = DT_IDFLG_USER;
		ubits = DT_IDFLG_USER;
		break;
	case DT_LINK_STATIC:
		kmask = umask = 0;
		kbits = ubits = -1u;
		break;
	default:
		xyerror(D_UNKNOWN, "internal error -- invalid link mode %u\n",
		    dtp->dt_linkmode);
	}

	assert(pcb->pcb_difo == NULL);
	pcb->pcb_difo = dt_zalloc(dtp, sizeof (dtrace_difo_t));

	if ((dp = pcb->pcb_difo) == NULL)
		longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);

	dp->dtdo_buf = dt_alloc(dtp, sizeof (dif_instr_t) * dlp->dl_len);

	if (dp->dtdo_buf == NULL)
		longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);

	if ((labels = dt_alloc(dtp, sizeof (uint_t) * dlp->dl_label)) == NULL)
		longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);

	/*
	 * Make an initial pass through the instruction list, filling in the
	 * instruction buffer with valid instructions and skipping labeled nops.
	 * While doing this, we also fill in our labels[] translation table
	 * and we count up the number of relocation table entries we will need.
	 */
	for (i = 0, dip = dlp->dl_list; dip != NULL; dip = dip->di_next) {
		if (dip->di_label != DT_LBL_NONE)
			labels[dip->di_label] = i;

		if (dip->di_label == DT_LBL_NONE ||
		    dip->di_instr != DIF_INSTR_NOP)
			dp->dtdo_buf[i++] = dip->di_instr;

		if (dip->di_extern == NULL)
			continue; /* no external references needed */

		switch (DIF_INSTR_OP(dip->di_instr)) {
		case DIF_OP_SETX:
			idp = dip->di_extern;
			if ((idp->di_flags & kmask) == kbits)
				krel++;
			else if ((idp->di_flags & umask) == ubits)
				urel++;
			break;
		case DIF_OP_XLATE:
		case DIF_OP_XLARG:
			xlrefs++;
			break;
		default:
			xyerror(D_UNKNOWN, "unexpected assembler relocation "
			    "for opcode 0x%x\n", DIF_INSTR_OP(dip->di_instr));
		}
	}

	assert(i == dlp->dl_len);
	dp->dtdo_len = dlp->dl_len;

	/*
	 * Make a second pass through the instructions, relocating each branch
	 * label to the index of the final instruction in the buffer and noting
	 * any other instruction-specific DIFO flags such as dtdo_destructive.
	 */
	for (i = 0; i < dp->dtdo_len; i++) {
		dif_instr_t instr = dp->dtdo_buf[i];
		uint_t op = DIF_INSTR_OP(instr);

		if (op == DIF_OP_CALL) {
			if (DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUT ||
			    DIF_INSTR_SUBR(instr) == DIF_SUBR_COPYOUTSTR)
				dp->dtdo_destructive = 1;
			continue;
		}

		if (op >= DIF_OP_BA && op <= DIF_OP_BLEU) {
			assert(DIF_INSTR_LABEL(instr) < dlp->dl_label);
			dp->dtdo_buf[i] = DIF_INSTR_BRANCH(op,
			    labels[DIF_INSTR_LABEL(instr)]);
		}
	}

	dt_free(dtp, labels);
	pcb->pcb_asvidx = 0;

	/*
	 * Allocate memory for the appropriate number of variable records and
	 * then fill in each variable record.  As we populate the variable
	 * table we insert the corresponding variable names into the strtab.
	 */
	(void) dt_idhash_iter(dtp->dt_tls, dt_countvar, &n);
	(void) dt_idhash_iter(dtp->dt_globals, dt_countvar, &n);
	(void) dt_idhash_iter(pcb->pcb_locals, dt_countvar, &n);

	if (n != 0) {
		dp->dtdo_vartab = dt_alloc(dtp, n * sizeof (dtrace_difv_t));
		dp->dtdo_varlen = (uint32_t)n;

		if (dp->dtdo_vartab == NULL)
			longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);

		(void) dt_idhash_iter(dtp->dt_tls, dt_copyvar, pcb);
		(void) dt_idhash_iter(dtp->dt_globals, dt_copyvar, pcb);
		(void) dt_idhash_iter(pcb->pcb_locals, dt_copyvar, pcb);
	}

	/*
	 * Allocate memory for the appropriate number of relocation table
	 * entries based upon our kernel and user counts from the first pass.
	 */
	if (krel != 0) {
		dp->dtdo_kreltab = dt_alloc(dtp,
		    krel * sizeof (dof_relodesc_t));
		dp->dtdo_krelen = krel;

		if (dp->dtdo_kreltab == NULL)
			longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);
	}

	if (urel != 0) {
		dp->dtdo_ureltab = dt_alloc(dtp,
		    urel * sizeof (dof_relodesc_t));
		dp->dtdo_urelen = urel;

		if (dp->dtdo_ureltab == NULL)
			longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);
	}

	if (xlrefs != 0) {
		dp->dtdo_xlmtab = dt_zalloc(dtp, sizeof (dt_node_t *) * xlrefs);
		dp->dtdo_xlmlen = xlrefs;

		if (dp->dtdo_xlmtab == NULL)
			longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);
	}

	/*
	 * If any relocations are needed, make another pass through the
	 * instruction list and fill in the relocation table entries.
	 */
	if (krel + urel + xlrefs != 0) {
		uint_t knodef = pcb->pcb_cflags & DTRACE_C_KNODEF;
		uint_t unodef = pcb->pcb_cflags & DTRACE_C_UNODEF;

		dof_relodesc_t *krp = dp->dtdo_kreltab;
		dof_relodesc_t *urp = dp->dtdo_ureltab;
		dt_node_t **xlp = dp->dtdo_xlmtab;

		i = 0; /* dtdo_buf[] index */

		for (dip = dlp->dl_list; dip != NULL; dip = dip->di_next) {
			dof_relodesc_t *rp;
			ssize_t soff;
			uint_t nodef;

			if (dip->di_label != DT_LBL_NONE &&
			    dip->di_instr == DIF_INSTR_NOP)
				continue; /* skip label declarations */

			i++; /* advance dtdo_buf[] index */

			if (DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLATE ||
			    DIF_INSTR_OP(dip->di_instr) == DIF_OP_XLARG) {
				assert(dp->dtdo_buf[i - 1] == dip->di_instr);
				dt_as_xlate(pcb, dp, i - 1, (uint_t)
				    (xlp++ - dp->dtdo_xlmtab), dip->di_extern);
				continue;
			}

			if ((idp = dip->di_extern) == NULL)
				continue; /* no relocation entry needed */

			if ((idp->di_flags & kmask) == kbits) {
				nodef = knodef;
				rp = krp++;
			} else if ((idp->di_flags & umask) == ubits) {
				nodef = unodef;
				rp = urp++;
			} else
				continue;

			if (!nodef)
				dt_as_undef(idp, i);

			assert(DIF_INSTR_OP(dip->di_instr) == DIF_OP_SETX);
			soff = dt_strtab_insert(pcb->pcb_strtab, idp->di_name);

			if (soff == -1L)
				longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);
			if (soff > DIF_STROFF_MAX)
				longjmp(pcb->pcb_jmpbuf, EDT_STR2BIG);

			rp->dofr_name = (dof_stridx_t)soff;
			rp->dofr_type = DOF_RELO_SETX;
			rp->dofr_offset = DIF_INSTR_INTEGER(dip->di_instr) *
			    sizeof (uint64_t);
			rp->dofr_data = 0;
		}

		assert(krp == dp->dtdo_kreltab + dp->dtdo_krelen);
		assert(urp == dp->dtdo_ureltab + dp->dtdo_urelen);
		assert(xlp == dp->dtdo_xlmtab + dp->dtdo_xlmlen);
		assert(i == dp->dtdo_len);
	}

	/*
	 * Allocate memory for the compiled string table and then copy the
	 * chunks from the string table into the final string buffer.
	 */
	if ((n = dt_strtab_size(pcb->pcb_strtab)) != 0) {
		if ((dp->dtdo_strtab = dt_alloc(dtp, n)) == NULL)
			longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);

		(void) dt_strtab_write(pcb->pcb_strtab,
		    (dt_strtab_write_f *)dt_copystr, pcb);
		dp->dtdo_strlen = (uint32_t)n;
	}

	/*
	 * Allocate memory for the compiled integer table and then copy the
	 * integer constants from the table into the final integer buffer.
	 */
	if ((n = dt_inttab_size(pcb->pcb_inttab)) != 0) {
		if ((dp->dtdo_inttab = dt_alloc(dtp,
		    n * sizeof (uint64_t))) == NULL)
			longjmp(pcb->pcb_jmpbuf, EDT_NOMEM);

		dt_inttab_write(pcb->pcb_inttab, dp->dtdo_inttab);
		dp->dtdo_intlen = (uint32_t)n;
	}

	/*
	 * Fill in the DIFO return type from the type associated with the
	 * node saved in pcb_dret, and then clear pcb_difo and pcb_dret
	 * now that the assembler has completed successfully.
	 */
	dt_node_diftype(dtp, pcb->pcb_dret, &dp->dtdo_rtype);
	pcb->pcb_difo = NULL;
	pcb->pcb_dret = NULL;

	if (pcb->pcb_cflags & DTRACE_C_DIFV)
		dt_dis(dp, stderr);

	return (dp);
}
Esempio n. 3
0
void
dt_dis(const dtrace_difo_t *dp, FILE *fp)
{
	static const struct opent {
		const char *op_name;
		void (*op_func)(const dtrace_difo_t *, const char *,
		    dif_instr_t, FILE *);
	} optab[] = {
		{ "(illegal opcode)", dt_dis_str },
		{ "or", dt_dis_log },		/* DIF_OP_OR */
		{ "xor", dt_dis_log },		/* DIF_OP_XOR */
		{ "and", dt_dis_log },		/* DIF_OP_AND */
		{ "sll", dt_dis_log },		/* DIF_OP_SLL */
		{ "srl", dt_dis_log },		/* DIF_OP_SRL */
		{ "sub", dt_dis_log },		/* DIF_OP_SUB */
		{ "add", dt_dis_log },		/* DIF_OP_ADD */
		{ "mul", dt_dis_log },		/* DIF_OP_MUL */
		{ "sdiv", dt_dis_log },		/* DIF_OP_SDIV */
		{ "udiv", dt_dis_log },		/* DIF_OP_UDIV */
		{ "srem", dt_dis_log },		/* DIF_OP_SREM */
		{ "urem", dt_dis_log },		/* DIF_OP_UREM */
		{ "not", dt_dis_r1rd },		/* DIF_OP_NOT */
		{ "mov", dt_dis_r1rd },		/* DIF_OP_MOV */
		{ "cmp", dt_dis_cmp },		/* DIF_OP_CMP */
		{ "tst", dt_dis_tst },		/* DIF_OP_TST */
		{ "ba", dt_dis_branch },	/* DIF_OP_BA */
		{ "be", dt_dis_branch },	/* DIF_OP_BE */
		{ "bne", dt_dis_branch },	/* DIF_OP_BNE */
		{ "bg", dt_dis_branch },	/* DIF_OP_BG */
		{ "bgu", dt_dis_branch },	/* DIF_OP_BGU */
		{ "bge", dt_dis_branch },	/* DIF_OP_BGE */
		{ "bgeu", dt_dis_branch },	/* DIF_OP_BGEU */
		{ "bl", dt_dis_branch },	/* DIF_OP_BL */
		{ "blu", dt_dis_branch },	/* DIF_OP_BLU */
		{ "ble", dt_dis_branch },	/* DIF_OP_BLE */
		{ "bleu", dt_dis_branch },	/* DIF_OP_BLEU */
		{ "ldsb", dt_dis_load },	/* DIF_OP_LDSB */
		{ "ldsh", dt_dis_load },	/* DIF_OP_LDSH */
		{ "ldsw", dt_dis_load },	/* DIF_OP_LDSW */
		{ "ldub", dt_dis_load },	/* DIF_OP_LDUB */
		{ "lduh", dt_dis_load },	/* DIF_OP_LDUH */
		{ "lduw", dt_dis_load },	/* DIF_OP_LDUW */
		{ "ldx", dt_dis_load },		/* DIF_OP_LDX */
		{ "ret", dt_dis_ret },		/* DIF_OP_RET */
		{ "nop", dt_dis_str },		/* DIF_OP_NOP */
		{ "setx", dt_dis_setx },	/* DIF_OP_SETX */
		{ "sets", dt_dis_sets },	/* DIF_OP_SETS */
		{ "scmp", dt_dis_cmp },		/* DIF_OP_SCMP */
		{ "ldga", dt_dis_lda },		/* DIF_OP_LDGA */
		{ "ldgs", dt_dis_ldv },		/* DIF_OP_LDGS */
		{ "stgs", dt_dis_stv },		/* DIF_OP_STGS */
		{ "ldta", dt_dis_lda },		/* DIF_OP_LDTA */
		{ "ldts", dt_dis_ldv },		/* DIF_OP_LDTS */
		{ "stts", dt_dis_stv },		/* DIF_OP_STTS */
		{ "sra", dt_dis_log },		/* DIF_OP_SRA */
		{ "call", dt_dis_call },	/* DIF_OP_CALL */
		{ "pushtr", dt_dis_pushts },	/* DIF_OP_PUSHTR */
		{ "pushtv", dt_dis_pushts },	/* DIF_OP_PUSHTV */
		{ "popts", dt_dis_str },	/* DIF_OP_POPTS */
		{ "flushts", dt_dis_str },	/* DIF_OP_FLUSHTS */
		{ "ldgaa", dt_dis_ldv },	/* DIF_OP_LDGAA */
		{ "ldtaa", dt_dis_ldv },	/* DIF_OP_LDTAA */
		{ "stgaa", dt_dis_stv },	/* DIF_OP_STGAA */
		{ "sttaa", dt_dis_stv },	/* DIF_OP_STTAA */
		{ "ldls", dt_dis_ldv },		/* DIF_OP_LDLS */
		{ "stls", dt_dis_stv },		/* DIF_OP_STLS */
		{ "allocs", dt_dis_r1rd },	/* DIF_OP_ALLOCS */
		{ "copys", dt_dis_log },	/* DIF_OP_COPYS */
		{ "stb", dt_dis_store },	/* DIF_OP_STB */
		{ "sth", dt_dis_store },	/* DIF_OP_STH */
		{ "stw", dt_dis_store },	/* DIF_OP_STW */
		{ "stx", dt_dis_store },	/* DIF_OP_STX */
		{ "uldsb", dt_dis_load },	/* DIF_OP_ULDSB */
		{ "uldsh", dt_dis_load },	/* DIF_OP_ULDSH */
		{ "uldsw", dt_dis_load },	/* DIF_OP_ULDSW */
		{ "uldub", dt_dis_load },	/* DIF_OP_ULDUB */
		{ "ulduh", dt_dis_load },	/* DIF_OP_ULDUH */
		{ "ulduw", dt_dis_load },	/* DIF_OP_ULDUW */
		{ "uldx", dt_dis_load },	/* DIF_OP_ULDX */
		{ "rldsb", dt_dis_load },	/* DIF_OP_RLDSB */
		{ "rldsh", dt_dis_load },	/* DIF_OP_RLDSH */
		{ "rldsw", dt_dis_load },	/* DIF_OP_RLDSW */
		{ "rldub", dt_dis_load },	/* DIF_OP_RLDUB */
		{ "rlduh", dt_dis_load },	/* DIF_OP_RLDUH */
		{ "rlduw", dt_dis_load },	/* DIF_OP_RLDUW */
		{ "rldx", dt_dis_load },	/* DIF_OP_RLDX */
		{ "xlate", dt_dis_xlate },	/* DIF_OP_XLATE */
		{ "xlarg", dt_dis_xlate },	/* DIF_OP_XLARG */
	};

	const struct opent *op;
	ulong_t i = 0;
	char type[DT_TYPE_NAMELEN];

	(void) fprintf(fp, "\nDIFO 0x%p returns %s\n", (void *)dp,
	    dt_dis_typestr(&dp->dtdo_rtype, type, sizeof (type)));

	(void) fprintf(fp, "%-3s %-8s    %s\n",
	    "OFF", "OPCODE", "INSTRUCTION");

	for (i = 0; i < dp->dtdo_len; i++) {
		dif_instr_t instr = dp->dtdo_buf[i];
		dif_instr_t opcode = DIF_INSTR_OP(instr);

		if (opcode >= sizeof (optab) / sizeof (optab[0]))
			opcode = 0; /* force invalid opcode message */

		op = &optab[opcode];
		(void) fprintf(fp, "%02lu: %08x    ", i, instr);
		op->op_func(dp, op->op_name, instr, fp);
		(void) fprintf(fp, "\n");
	}

	if (dp->dtdo_varlen != 0) {
		(void) fprintf(fp, "\n%-16s %-4s %-3s %-3s %-4s %s\n",
		    "NAME", "ID", "KND", "SCP", "FLAG", "TYPE");
	}

	for (i = 0; i < dp->dtdo_varlen; i++) {
		dtrace_difv_t *v = &dp->dtdo_vartab[i];
		char kind[4], scope[4], flags[16] = { 0 };

		switch (v->dtdv_kind) {
		case DIFV_KIND_ARRAY:
			(void) strcpy(kind, "arr");
			break;
		case DIFV_KIND_SCALAR:
			(void) strcpy(kind, "scl");
			break;
		default:
			(void) snprintf(kind, sizeof (kind),
			    "%u", v->dtdv_kind);
		}

		switch (v->dtdv_scope) {
		case DIFV_SCOPE_GLOBAL:
			(void) strcpy(scope, "glb");
			break;
		case DIFV_SCOPE_THREAD:
			(void) strcpy(scope, "tls");
			break;
		case DIFV_SCOPE_LOCAL:
			(void) strcpy(scope, "loc");
			break;
		default:
			(void) snprintf(scope, sizeof (scope),
			    "%u", v->dtdv_scope);
		}

		if (v->dtdv_flags & ~(DIFV_F_REF | DIFV_F_MOD)) {
			(void) snprintf(flags, sizeof (flags), "/0x%x",
			    v->dtdv_flags & ~(DIFV_F_REF | DIFV_F_MOD));
		}

		if (v->dtdv_flags & DIFV_F_REF)
			(void) strcat(flags, "/r");
		if (v->dtdv_flags & DIFV_F_MOD)
			(void) strcat(flags, "/w");

		(void) fprintf(fp, "%-16s %-4x %-3s %-3s %-4s %s\n",
		    &dp->dtdo_strtab[v->dtdv_name],
		    v->dtdv_id, kind, scope, flags + 1,
		    dt_dis_typestr(&v->dtdv_type, type, sizeof (type)));
	}

	if (dp->dtdo_xlmlen != 0) {
		(void) fprintf(fp, "\n%-4s %-3s %-12s %s\n",
		    "XLID", "ARG", "MEMBER", "TYPE");
	}

	for (i = 0; i < dp->dtdo_xlmlen; i++) {
		dt_node_t *dnp = dp->dtdo_xlmtab[i];
		dt_xlator_t *dxp = dnp->dn_membexpr->dn_xlator;
		(void) fprintf(fp, "%-4u %-3d %-12s %s\n",
		    (uint_t)dxp->dx_id, dxp->dx_arg, dnp->dn_membname,
		    dt_node_type_name(dnp, type, sizeof (type)));
	}

	if (dp->dtdo_krelen != 0)
		dt_dis_rtab("KREL", dp, fp, dp->dtdo_kreltab, dp->dtdo_krelen);

	if (dp->dtdo_urelen != 0)
		dt_dis_rtab("UREL", dp, fp, dp->dtdo_ureltab, dp->dtdo_urelen);
}
Esempio n. 4
0
static int
dis(uintptr_t addr, dtrace_difo_t *dp)
{
	static const struct opent {
		const char *op_name;
		void (*op_func)(const dtrace_difo_t *,
		    const char *, dif_instr_t);
	} optab[] = {
		{ "(illegal opcode)", dis_str },
		{ "or", dis_log },		/* DIF_OP_OR */
		{ "xor", dis_log },		/* DIF_OP_XOR */
		{ "and", dis_log },		/* DIF_OP_AND */
		{ "sll", dis_log },		/* DIF_OP_SLL */
		{ "srl", dis_log },		/* DIF_OP_SRL */
		{ "sub", dis_log },		/* DIF_OP_SUB */
		{ "add", dis_log },		/* DIF_OP_ADD */
		{ "mul", dis_log },		/* DIF_OP_MUL */
		{ "sdiv", dis_log },		/* DIF_OP_SDIV */
		{ "udiv", dis_log },		/* DIF_OP_UDIV */
		{ "srem", dis_log },		/* DIF_OP_SREM */
		{ "urem", dis_log },		/* DIF_OP_UREM */
		{ "not", dis_r1rd },		/* DIF_OP_NOT */
		{ "mov", dis_r1rd },		/* DIF_OP_MOV */
		{ "cmp", dis_cmp },		/* DIF_OP_CMP */
		{ "tst", dis_tst },		/* DIF_OP_TST */
		{ "ba", dis_branch },		/* DIF_OP_BA */
		{ "be", dis_branch },		/* DIF_OP_BE */
		{ "bne", dis_branch },		/* DIF_OP_BNE */
		{ "bg", dis_branch },		/* DIF_OP_BG */
		{ "bgu", dis_branch },		/* DIF_OP_BGU */
		{ "bge", dis_branch },		/* DIF_OP_BGE */
		{ "bgeu", dis_branch },		/* DIF_OP_BGEU */
		{ "bl", dis_branch },		/* DIF_OP_BL */
		{ "blu", dis_branch },		/* DIF_OP_BLU */
		{ "ble", dis_branch },		/* DIF_OP_BLE */
		{ "bleu", dis_branch },		/* DIF_OP_BLEU */
		{ "ldsb", dis_load },		/* DIF_OP_LDSB */
		{ "ldsh", dis_load },		/* DIF_OP_LDSH */
		{ "ldsw", dis_load },		/* DIF_OP_LDSW */
		{ "ldub", dis_load },		/* DIF_OP_LDUB */
		{ "lduh", dis_load },		/* DIF_OP_LDUH */
		{ "lduw", dis_load },		/* DIF_OP_LDUW */
		{ "ldx", dis_load },		/* DIF_OP_LDX */
		{ "ret", dis_ret },		/* DIF_OP_RET */
		{ "nop", dis_str },		/* DIF_OP_NOP */
		{ "setx", dis_setx },		/* DIF_OP_SETX */
		{ "sets", dis_sets },		/* DIF_OP_SETS */
		{ "scmp", dis_cmp },		/* DIF_OP_SCMP */
		{ "ldga", dis_lda },		/* DIF_OP_LDGA */
		{ "ldgs", dis_ldv },		/* DIF_OP_LDGS */
		{ "stgs", dis_stv },		/* DIF_OP_STGS */
		{ "ldta", dis_lda },		/* DIF_OP_LDTA */
		{ "ldts", dis_ldv },		/* DIF_OP_LDTS */
		{ "stts", dis_stv },		/* DIF_OP_STTS */
		{ "sra", dis_log },		/* DIF_OP_SRA */
		{ "call", dis_call },		/* DIF_OP_CALL */
		{ "pushtr", dis_pushts },	/* DIF_OP_PUSHTR */
		{ "pushtv", dis_pushts },	/* DIF_OP_PUSHTV */
		{ "popts", dis_str },		/* DIF_OP_POPTS */
		{ "flushts", dis_str },		/* DIF_OP_FLUSHTS */
		{ "ldgaa", dis_ldv },		/* DIF_OP_LDGAA */
		{ "ldtaa", dis_ldv },		/* DIF_OP_LDTAA */
		{ "stgaa", dis_stv },		/* DIF_OP_STGAA */
		{ "sttaa", dis_stv },		/* DIF_OP_STTAA */
		{ "ldls", dis_ldv },		/* DIF_OP_LDLS */
		{ "stls", dis_stv },		/* DIF_OP_STLS */
		{ "allocs", dis_r1rd },		/* DIF_OP_ALLOCS */
		{ "copys", dis_log },		/* DIF_OP_COPYS */
		{ "stb", dis_store },		/* DIF_OP_STB */
		{ "sth", dis_store },		/* DIF_OP_STH */
		{ "stw", dis_store },		/* DIF_OP_STW */
		{ "stx", dis_store },		/* DIF_OP_STX */
		{ "uldsb", dis_load },		/* DIF_OP_ULDSB */
		{ "uldsh", dis_load },		/* DIF_OP_ULDSH */
		{ "uldsw", dis_load },		/* DIF_OP_ULDSW */
		{ "uldub", dis_load },		/* DIF_OP_ULDUB */
		{ "ulduh", dis_load },		/* DIF_OP_ULDUH */
		{ "ulduw", dis_load },		/* DIF_OP_ULDUW */
		{ "uldx", dis_load },		/* DIF_OP_ULDX */
		{ "rldsb", dis_load },		/* DIF_OP_RLDSB */
		{ "rldsh", dis_load },		/* DIF_OP_RLDSH */
		{ "rldsw", dis_load },		/* DIF_OP_RLDSW */
		{ "rldub", dis_load },		/* DIF_OP_RLDUB */
		{ "rlduh", dis_load },		/* DIF_OP_RLDUH */
		{ "rlduw", dis_load },		/* DIF_OP_RLDUW */
		{ "rldx", dis_load },		/* DIF_OP_RLDX */
		{ "xlate", dis_xlate },		/* DIF_OP_XLATE */
		{ "xlarg", dis_xlate },		/* DIF_OP_XLARG */
	};

	dif_instr_t instr, opcode;
	const struct opent *op;

	if (mdb_vread(&instr, sizeof (dif_instr_t), addr) == -1) {
		mdb_warn("failed to read DIF instruction at %p", addr);
		return (DCMD_ERR);
	}

	opcode = DIF_INSTR_OP(instr);

	if (opcode >= sizeof (optab) / sizeof (optab[0]))
		opcode = 0; /* force invalid opcode message */

	op = &optab[opcode];
	mdb_printf("%0?p %08x ", addr, instr);
	op->op_func(dp, op->op_name, instr);
	mdb_printf("\n");
	mdb_set_dot(addr + sizeof (dif_instr_t));

	return (DCMD_OK);
}