xref: /onnv-gate/usr/src/cmd/mdb/intel/mdb/mdb_amd64util.c (revision 0:68f95e015346)
1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * CDDL HEADER START
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*0Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*0Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*0Sstevel@tonic-gate  * with the License.
8*0Sstevel@tonic-gate  *
9*0Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*0Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*0Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*0Sstevel@tonic-gate  * and limitations under the License.
13*0Sstevel@tonic-gate  *
14*0Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*0Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*0Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*0Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*0Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*0Sstevel@tonic-gate  *
20*0Sstevel@tonic-gate  * CDDL HEADER END
21*0Sstevel@tonic-gate  */
22*0Sstevel@tonic-gate /*
23*0Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24*0Sstevel@tonic-gate  * Use is subject to license terms.
25*0Sstevel@tonic-gate  */
26*0Sstevel@tonic-gate 
27*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #include <sys/types.h>
30*0Sstevel@tonic-gate #include <sys/reg.h>
31*0Sstevel@tonic-gate #include <sys/privregs.h>
32*0Sstevel@tonic-gate #include <sys/stack.h>
33*0Sstevel@tonic-gate #include <sys/frame.h>
34*0Sstevel@tonic-gate 
35*0Sstevel@tonic-gate #include <mdb/mdb_target_impl.h>
36*0Sstevel@tonic-gate #include <mdb/mdb_kreg_impl.h>
37*0Sstevel@tonic-gate #include <mdb/mdb_debug.h>
38*0Sstevel@tonic-gate #include <mdb/mdb_modapi.h>
39*0Sstevel@tonic-gate #include <mdb/mdb_amd64util.h>
40*0Sstevel@tonic-gate #include <mdb/mdb_err.h>
41*0Sstevel@tonic-gate #include <mdb/mdb.h>
42*0Sstevel@tonic-gate 
43*0Sstevel@tonic-gate /*
44*0Sstevel@tonic-gate  * This array is used by the getareg and putareg entry points, and also by our
45*0Sstevel@tonic-gate  * register variable discipline.
46*0Sstevel@tonic-gate  */
47*0Sstevel@tonic-gate 
48*0Sstevel@tonic-gate const mdb_tgt_regdesc_t mdb_amd64_kregs[] = {
49*0Sstevel@tonic-gate 	{ "savfp", KREG_SAVFP, MDB_TGT_R_EXPORT },
50*0Sstevel@tonic-gate 	{ "savpc", KREG_SAVPC, MDB_TGT_R_EXPORT },
51*0Sstevel@tonic-gate 	{ "rdi", KREG_RDI, MDB_TGT_R_EXPORT },
52*0Sstevel@tonic-gate 	{ "rsi", KREG_RSI, MDB_TGT_R_EXPORT },
53*0Sstevel@tonic-gate 	{ "rdx", KREG_RDX, MDB_TGT_R_EXPORT },
54*0Sstevel@tonic-gate 	{ "rcx", KREG_RCX, MDB_TGT_R_EXPORT },
55*0Sstevel@tonic-gate 	{ "r8", KREG_R8, MDB_TGT_R_EXPORT },
56*0Sstevel@tonic-gate 	{ "r9", KREG_R9, MDB_TGT_R_EXPORT },
57*0Sstevel@tonic-gate 	{ "rax", KREG_RAX, MDB_TGT_R_EXPORT },
58*0Sstevel@tonic-gate 	{ "rbx", KREG_RBX, MDB_TGT_R_EXPORT },
59*0Sstevel@tonic-gate 	{ "rbp", KREG_RBP, MDB_TGT_R_EXPORT },
60*0Sstevel@tonic-gate 	{ "r10", KREG_R10, MDB_TGT_R_EXPORT },
61*0Sstevel@tonic-gate 	{ "r11", KREG_R11, MDB_TGT_R_EXPORT },
62*0Sstevel@tonic-gate 	{ "r12", KREG_R12, MDB_TGT_R_EXPORT },
63*0Sstevel@tonic-gate 	{ "r13", KREG_R13, MDB_TGT_R_EXPORT },
64*0Sstevel@tonic-gate 	{ "r14", KREG_R14, MDB_TGT_R_EXPORT },
65*0Sstevel@tonic-gate 	{ "r15", KREG_R15, MDB_TGT_R_EXPORT },
66*0Sstevel@tonic-gate 	{ "fsbase", KREG_FSBASE, MDB_TGT_R_EXPORT | MDB_TGT_R_PRIV },
67*0Sstevel@tonic-gate 	{ "gsbase", KREG_GSBASE, MDB_TGT_R_EXPORT | MDB_TGT_R_PRIV },
68*0Sstevel@tonic-gate 	{ "kgsbase", KREG_KGSBASE, MDB_TGT_R_EXPORT | MDB_TGT_R_PRIV },
69*0Sstevel@tonic-gate 	{ "ds", KREG_DS, MDB_TGT_R_EXPORT },
70*0Sstevel@tonic-gate 	{ "es", KREG_ES, MDB_TGT_R_EXPORT },
71*0Sstevel@tonic-gate 	{ "fs", KREG_FS, MDB_TGT_R_EXPORT },
72*0Sstevel@tonic-gate 	{ "gs", KREG_GS, MDB_TGT_R_EXPORT },
73*0Sstevel@tonic-gate 	{ "trapno", KREG_TRAPNO, MDB_TGT_R_EXPORT | MDB_TGT_R_PRIV },
74*0Sstevel@tonic-gate 	{ "err", KREG_ERR, MDB_TGT_R_EXPORT | MDB_TGT_R_PRIV },
75*0Sstevel@tonic-gate 	{ "rip", KREG_RIP, MDB_TGT_R_EXPORT },
76*0Sstevel@tonic-gate 	{ "cs", KREG_CS, MDB_TGT_R_EXPORT },
77*0Sstevel@tonic-gate 	{ "rflags", KREG_RFLAGS, MDB_TGT_R_EXPORT },
78*0Sstevel@tonic-gate 	{ "rsp", KREG_RSP, MDB_TGT_R_EXPORT },
79*0Sstevel@tonic-gate 	{ "ss", KREG_SS, MDB_TGT_R_EXPORT },
80*0Sstevel@tonic-gate 	{ NULL, 0, 0 }
81*0Sstevel@tonic-gate };
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate void
84*0Sstevel@tonic-gate mdb_amd64_printregs(const mdb_tgt_gregset_t *gregs)
85*0Sstevel@tonic-gate {
86*0Sstevel@tonic-gate 	const kreg_t *kregs = &gregs->kregs[0];
87*0Sstevel@tonic-gate 	kreg_t rflags = kregs[KREG_RFLAGS];
88*0Sstevel@tonic-gate 
89*0Sstevel@tonic-gate #define	GETREG2(x) ((uintptr_t)kregs[(x)]), ((uintptr_t)kregs[(x)])
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate 	mdb_printf("%%rax = 0x%0?p %15A %%r9  = 0x%0?p %A\n",
92*0Sstevel@tonic-gate 	    GETREG2(KREG_RAX), GETREG2(KREG_R9));
93*0Sstevel@tonic-gate 	mdb_printf("%%rbx = 0x%0?p %15A %%r10 = 0x%0?p %A\n",
94*0Sstevel@tonic-gate 	    GETREG2(KREG_RBX), GETREG2(KREG_R10));
95*0Sstevel@tonic-gate 	mdb_printf("%%rcx = 0x%0?p %15A %%r11 = 0x%0?p %A\n",
96*0Sstevel@tonic-gate 	    GETREG2(KREG_RCX), GETREG2(KREG_R11));
97*0Sstevel@tonic-gate 	mdb_printf("%%rdx = 0x%0?p %15A %%r12 = 0x%0?p %A\n",
98*0Sstevel@tonic-gate 	    GETREG2(KREG_RDX), GETREG2(KREG_R12));
99*0Sstevel@tonic-gate 	mdb_printf("%%rsi = 0x%0?p %15A %%r13 = 0x%0?p %A\n",
100*0Sstevel@tonic-gate 	    GETREG2(KREG_RSI), GETREG2(KREG_R13));
101*0Sstevel@tonic-gate 	mdb_printf("%%rdi = 0x%0?p %15A %%r14 = 0x%0?p %A\n",
102*0Sstevel@tonic-gate 	    GETREG2(KREG_RDI), GETREG2(KREG_R14));
103*0Sstevel@tonic-gate 	mdb_printf("%%r8  = 0x%0?p %15A %%r15 = 0x%0?p %A\n\n",
104*0Sstevel@tonic-gate 	    GETREG2(KREG_R8), GETREG2(KREG_R15));
105*0Sstevel@tonic-gate 
106*0Sstevel@tonic-gate 	mdb_printf("%%rip = 0x%0?p %A\n", GETREG2(KREG_RIP));
107*0Sstevel@tonic-gate 	mdb_printf("%%rbp = 0x%0?p\n", kregs[KREG_RBP]);
108*0Sstevel@tonic-gate 	mdb_printf("%%rsp = 0x%0?p\n", kregs[KREG_RSP]);
109*0Sstevel@tonic-gate 
110*0Sstevel@tonic-gate 	mdb_printf("%%rflags = 0x%08x\n", rflags);
111*0Sstevel@tonic-gate 
112*0Sstevel@tonic-gate 	mdb_printf("  id=%u vip=%u vif=%u ac=%u vm=%u rf=%u nt=%u iopl=0x%x\n",
113*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_ID_MASK) >> KREG_EFLAGS_ID_SHIFT,
114*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_VIP_MASK) >> KREG_EFLAGS_VIP_SHIFT,
115*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_VIF_MASK) >> KREG_EFLAGS_VIF_SHIFT,
116*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_AC_MASK) >> KREG_EFLAGS_AC_SHIFT,
117*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_VM_MASK) >> KREG_EFLAGS_VM_SHIFT,
118*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_RF_MASK) >> KREG_EFLAGS_RF_SHIFT,
119*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_NT_MASK) >> KREG_EFLAGS_NT_SHIFT,
120*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_IOPL_MASK) >> KREG_EFLAGS_IOPL_SHIFT);
121*0Sstevel@tonic-gate 
122*0Sstevel@tonic-gate 	mdb_printf("  status=<%s,%s,%s,%s,%s,%s,%s,%s,%s>\n\n",
123*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_OF_MASK) ? "OF" : "of",
124*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_DF_MASK) ? "DF" : "df",
125*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_IF_MASK) ? "IF" : "if",
126*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_TF_MASK) ? "TF" : "tf",
127*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_SF_MASK) ? "SF" : "sf",
128*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_ZF_MASK) ? "ZF" : "zf",
129*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_AF_MASK) ? "AF" : "af",
130*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_PF_MASK) ? "PF" : "pf",
131*0Sstevel@tonic-gate 	    (rflags & KREG_EFLAGS_CF_MASK) ? "CF" : "cf");
132*0Sstevel@tonic-gate 
133*0Sstevel@tonic-gate 	mdb_printf("%24s%%cs = 0x%04x\t%%ds = 0x%04x\t%%es = 0x%04x\n",
134*0Sstevel@tonic-gate 	    " ", kregs[KREG_CS], kregs[KREG_DS], kregs[KREG_ES]);
135*0Sstevel@tonic-gate 
136*0Sstevel@tonic-gate 	mdb_printf("%%trapno = 0x%x\t\t%%fs = 0x%04x\tfsbase = 0x%0?p\n",
137*0Sstevel@tonic-gate 	    kregs[KREG_TRAPNO], (kregs[KREG_FS] & 0xffff), kregs[KREG_FSBASE]);
138*0Sstevel@tonic-gate 	mdb_printf("   %%err = 0x%x\t\t%%gs = 0x%04x\tgsbase = 0x%0?p\n",
139*0Sstevel@tonic-gate 	    kregs[KREG_ERR], (kregs[KREG_GS] & 0xffff), kregs[KREG_GSBASE]);
140*0Sstevel@tonic-gate }
141*0Sstevel@tonic-gate 
142*0Sstevel@tonic-gate int
143*0Sstevel@tonic-gate mdb_amd64_kvm_stack_iter(mdb_tgt_t *t, const mdb_tgt_gregset_t *gsp,
144*0Sstevel@tonic-gate     mdb_tgt_stack_f *func, void *arg)
145*0Sstevel@tonic-gate {
146*0Sstevel@tonic-gate 	mdb_tgt_gregset_t gregs;
147*0Sstevel@tonic-gate 	kreg_t *kregs = &gregs.kregs[0];
148*0Sstevel@tonic-gate 	int got_pc = (gsp->kregs[KREG_RIP] != 0);
149*0Sstevel@tonic-gate 
150*0Sstevel@tonic-gate 	struct {
151*0Sstevel@tonic-gate 		uintptr_t fr_savfp;
152*0Sstevel@tonic-gate 		uintptr_t fr_savpc;
153*0Sstevel@tonic-gate 	} fr;
154*0Sstevel@tonic-gate 
155*0Sstevel@tonic-gate 	uintptr_t fp = gsp->kregs[KREG_RBP];
156*0Sstevel@tonic-gate 	uintptr_t pc = gsp->kregs[KREG_RIP];
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 	bcopy(gsp, &gregs, sizeof (gregs));
159*0Sstevel@tonic-gate 
160*0Sstevel@tonic-gate 	while (fp != 0) {
161*0Sstevel@tonic-gate 
162*0Sstevel@tonic-gate 		if (fp & (STACK_ALIGN - 1))
163*0Sstevel@tonic-gate 			return (set_errno(EMDB_STKALIGN));
164*0Sstevel@tonic-gate 
165*0Sstevel@tonic-gate 		bzero(&fr, sizeof (fr));
166*0Sstevel@tonic-gate 		(void) mdb_tgt_vread(t, &fr, sizeof (fr), fp);
167*0Sstevel@tonic-gate 
168*0Sstevel@tonic-gate 		if (got_pc && func(arg, pc, 0, NULL, &gregs) != 0)
169*0Sstevel@tonic-gate 			break;
170*0Sstevel@tonic-gate 
171*0Sstevel@tonic-gate 		kregs[KREG_RSP] = kregs[KREG_RBP];
172*0Sstevel@tonic-gate 
173*0Sstevel@tonic-gate 		kregs[KREG_RBP] = fp = fr.fr_savfp;
174*0Sstevel@tonic-gate 		kregs[KREG_RIP] = pc = fr.fr_savpc;
175*0Sstevel@tonic-gate 
176*0Sstevel@tonic-gate 		got_pc = (pc != 0);
177*0Sstevel@tonic-gate 	}
178*0Sstevel@tonic-gate 
179*0Sstevel@tonic-gate 	return (0);
180*0Sstevel@tonic-gate }
181*0Sstevel@tonic-gate 
182*0Sstevel@tonic-gate /*
183*0Sstevel@tonic-gate  * Determine the return address for the current frame.  Typically this is the
184*0Sstevel@tonic-gate  * fr_savpc value from the current frame, but we also perform some special
185*0Sstevel@tonic-gate  * handling to see if we are stopped on one of the first two instructions of
186*0Sstevel@tonic-gate  * a typical function prologue, in which case %rbp will not be set up yet.
187*0Sstevel@tonic-gate  */
188*0Sstevel@tonic-gate int
189*0Sstevel@tonic-gate mdb_amd64_step_out(mdb_tgt_t *t, uintptr_t *p, kreg_t pc, kreg_t fp, kreg_t sp,
190*0Sstevel@tonic-gate     mdb_instr_t curinstr)
191*0Sstevel@tonic-gate {
192*0Sstevel@tonic-gate 	struct frame fr;
193*0Sstevel@tonic-gate 	GElf_Sym s;
194*0Sstevel@tonic-gate 	char buf[1];
195*0Sstevel@tonic-gate 
196*0Sstevel@tonic-gate 	enum {
197*0Sstevel@tonic-gate 		M_PUSHQ_RBP	= 0x55,	/* pushq %rbp */
198*0Sstevel@tonic-gate 		M_REX_W		= 0x48, /* REX prefix with only W set */
199*0Sstevel@tonic-gate 		M_MOVL_RBP	= 0x8b	/* movq %rsp, %rbp with prefix */
200*0Sstevel@tonic-gate 	};
201*0Sstevel@tonic-gate 
202*0Sstevel@tonic-gate 	if (mdb_tgt_lookup_by_addr(t, pc, MDB_TGT_SYM_FUZZY,
203*0Sstevel@tonic-gate 	    buf, 0, &s, NULL) == 0) {
204*0Sstevel@tonic-gate 		if (pc == s.st_value && curinstr == M_PUSHQ_RBP)
205*0Sstevel@tonic-gate 			fp = sp - 8;
206*0Sstevel@tonic-gate 		else if (pc == s.st_value + 1 && curinstr == M_REX_W) {
207*0Sstevel@tonic-gate 			if (mdb_tgt_vread(t, &curinstr, sizeof (curinstr),
208*0Sstevel@tonic-gate 			    pc + 1) == sizeof (curinstr) && curinstr ==
209*0Sstevel@tonic-gate 			    M_MOVL_RBP)
210*0Sstevel@tonic-gate 				fp = sp;
211*0Sstevel@tonic-gate 		}
212*0Sstevel@tonic-gate 	}
213*0Sstevel@tonic-gate 
214*0Sstevel@tonic-gate 	if (mdb_tgt_vread(t, &fr, sizeof (fr), fp) == sizeof (fr)) {
215*0Sstevel@tonic-gate 		*p = fr.fr_savpc;
216*0Sstevel@tonic-gate 		return (0);
217*0Sstevel@tonic-gate 	}
218*0Sstevel@tonic-gate 
219*0Sstevel@tonic-gate 	return (-1); /* errno is set for us */
220*0Sstevel@tonic-gate }
221*0Sstevel@tonic-gate 
222*0Sstevel@tonic-gate /*ARGSUSED*/
223*0Sstevel@tonic-gate int
224*0Sstevel@tonic-gate mdb_amd64_next(mdb_tgt_t *t, uintptr_t *p, kreg_t pc, mdb_instr_t curinstr)
225*0Sstevel@tonic-gate {
226*0Sstevel@tonic-gate 	mdb_tgt_addr_t npc;
227*0Sstevel@tonic-gate 
228*0Sstevel@tonic-gate 	enum {
229*0Sstevel@tonic-gate 		M_CALL_REL = 0xe8, /* call near with relative displacement */
230*0Sstevel@tonic-gate 		M_CALL_REG = 0xff, /* call near indirect or call far register */
231*0Sstevel@tonic-gate 
232*0Sstevel@tonic-gate 		M_REX_LO = 0x40,
233*0Sstevel@tonic-gate 		M_REX_HI = 0x4f
234*0Sstevel@tonic-gate 	};
235*0Sstevel@tonic-gate 
236*0Sstevel@tonic-gate 	/*
237*0Sstevel@tonic-gate 	 * If the opcode is a near call with relative displacement, assume the
238*0Sstevel@tonic-gate 	 * displacement is a rel32 from the next instruction.
239*0Sstevel@tonic-gate 	 */
240*0Sstevel@tonic-gate 	if (curinstr == M_CALL_REL) {
241*0Sstevel@tonic-gate 		*p = pc + sizeof (mdb_instr_t) + sizeof (uint32_t);
242*0Sstevel@tonic-gate 		return (0);
243*0Sstevel@tonic-gate 	}
244*0Sstevel@tonic-gate 
245*0Sstevel@tonic-gate 	/* Skip the rex prefix, if any */
246*0Sstevel@tonic-gate 	if (curinstr >= M_REX_LO && curinstr <= M_REX_HI &&
247*0Sstevel@tonic-gate 	    mdb_tgt_vread(t, &curinstr, sizeof (curinstr), pc) !=
248*0Sstevel@tonic-gate 	    sizeof (curinstr))
249*0Sstevel@tonic-gate 		return (-1); /* errno is set for us */
250*0Sstevel@tonic-gate 
251*0Sstevel@tonic-gate 	if (curinstr != M_CALL_REG) {
252*0Sstevel@tonic-gate 		/* It's not a call */
253*0Sstevel@tonic-gate 		return (set_errno(EAGAIN));
254*0Sstevel@tonic-gate 	}
255*0Sstevel@tonic-gate 
256*0Sstevel@tonic-gate 	if ((npc = mdb_dis_nextins(mdb.m_disasm, t, MDB_TGT_AS_VIRT, pc)) == pc)
257*0Sstevel@tonic-gate 		return (-1); /* errno is set for us */
258*0Sstevel@tonic-gate 
259*0Sstevel@tonic-gate 	*p = npc;
260*0Sstevel@tonic-gate 	return (0);
261*0Sstevel@tonic-gate }
262*0Sstevel@tonic-gate 
263*0Sstevel@tonic-gate /*ARGSUSED*/
264*0Sstevel@tonic-gate int
265*0Sstevel@tonic-gate mdb_amd64_kvm_frame(void *arglim, uintptr_t pc, uint_t argc, const long *argv,
266*0Sstevel@tonic-gate     const mdb_tgt_gregset_t *gregs)
267*0Sstevel@tonic-gate {
268*0Sstevel@tonic-gate 	argc = MIN(argc, (uintptr_t)arglim);
269*0Sstevel@tonic-gate 	mdb_printf("%a(", pc);
270*0Sstevel@tonic-gate 
271*0Sstevel@tonic-gate 	if (argc != 0) {
272*0Sstevel@tonic-gate 		mdb_printf("%lr", *argv++);
273*0Sstevel@tonic-gate 		for (argc--; argc != 0; argc--)
274*0Sstevel@tonic-gate 			mdb_printf(", %lr", *argv++);
275*0Sstevel@tonic-gate 	}
276*0Sstevel@tonic-gate 
277*0Sstevel@tonic-gate 	mdb_printf(")\n");
278*0Sstevel@tonic-gate 	return (0);
279*0Sstevel@tonic-gate }
280*0Sstevel@tonic-gate 
281*0Sstevel@tonic-gate int
282*0Sstevel@tonic-gate mdb_amd64_kvm_framev(void *arglim, uintptr_t pc, uint_t argc, const long *argv,
283*0Sstevel@tonic-gate     const mdb_tgt_gregset_t *gregs)
284*0Sstevel@tonic-gate {
285*0Sstevel@tonic-gate 	argc = MIN(argc, (uintptr_t)arglim);
286*0Sstevel@tonic-gate 	mdb_printf("%0?lr %a(", gregs->kregs[KREG_RBP], pc);
287*0Sstevel@tonic-gate 
288*0Sstevel@tonic-gate 	if (argc != 0) {
289*0Sstevel@tonic-gate 		mdb_printf("%lr", *argv++);
290*0Sstevel@tonic-gate 		for (argc--; argc != 0; argc--)
291*0Sstevel@tonic-gate 			mdb_printf(", %lr", *argv++);
292*0Sstevel@tonic-gate 	}
293*0Sstevel@tonic-gate 
294*0Sstevel@tonic-gate 	mdb_printf(")\n");
295*0Sstevel@tonic-gate 	return (0);
296*0Sstevel@tonic-gate }
297