xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_modapi.c (revision b0bb0d63258be430b0e22afcb1581974bd7b568e)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Copyright (c) 2013 by Delphix. All rights reserved.
25  * Copyright 2016 Nexenta Systems, Inc.  All rights reserved.
26  * Copyright 2019 Joyent, Inc.
27  * Copyright 2022 Oxide Computer Company
28  */
29 
30 #include <mdb/mdb_modapi.h>
31 #include <mdb/mdb_module.h>
32 #include <mdb/mdb_string.h>
33 #include <mdb/mdb_debug.h>
34 #include <mdb/mdb_callb.h>
35 #include <mdb/mdb_dump.h>
36 #include <mdb/mdb_err.h>
37 #include <mdb/mdb_io.h>
38 #include <mdb/mdb_lex.h>
39 #include <mdb/mdb_frame.h>
40 #include <mdb/mdb.h>
41 #include <inttypes.h>
42 
43 /*
44  * Private callback structure for implementing mdb_walk_dcmd, below.
45  */
46 typedef struct {
47 	mdb_idcmd_t *dw_dcmd;
48 	mdb_argvec_t dw_argv;
49 	uint_t dw_flags;
50 } dcmd_walk_arg_t;
51 
52 /*
53  * Global properties which modules are allowed to look at.  These are
54  * re-initialized by the target activation callbacks.
55  */
56 int mdb_prop_postmortem = FALSE;	/* Are we examining a dump? */
57 int mdb_prop_kernel = FALSE;		/* Are we examining a kernel? */
58 int mdb_prop_datamodel = 0;		/* Data model (see mdb_target_impl.h) */
59 
60 static int
61 call_idcmd(mdb_idcmd_t *idcp, uintmax_t addr, uintmax_t count,
62     uint_t flags, mdb_argvec_t *argv);
63 
64 int
65 mdb_snprintfrac(char *buf, int len,
66     uint64_t numerator, uint64_t denom, int frac_digits)
67 {
68 	int mul = 1;
69 	int whole, frac, i;
70 
71 	for (i = frac_digits; i; i--)
72 		mul *= 10;
73 	whole = numerator / denom;
74 	frac = mul * numerator / denom - mul * whole;
75 	return (mdb_snprintf(buf, len, "%u.%0*u", whole, frac_digits, frac));
76 }
77 
78 void
79 mdb_nicenum(uint64_t num, char *buf)
80 {
81 	uint64_t n = num;
82 	int index = 0;
83 	char *u;
84 
85 	while (n >= 1024) {
86 		n = (n + (1024 / 2)) / 1024; /* Round up or down */
87 		index++;
88 	}
89 
90 	u = &" \0K\0M\0G\0T\0P\0E\0"[index*2];
91 
92 	if (index == 0) {
93 		(void) mdb_snprintf(buf, MDB_NICENUM_BUFLEN, "%llu",
94 		    (u_longlong_t)n);
95 	} else if (n < 10 && (num & (num - 1)) != 0) {
96 		(void) mdb_snprintfrac(buf, MDB_NICENUM_BUFLEN,
97 		    num, 1ULL << 10 * index, 2);
98 		(void) strcat(buf, u);
99 	} else if (n < 100 && (num & (num - 1)) != 0) {
100 		(void) mdb_snprintfrac(buf, MDB_NICENUM_BUFLEN,
101 		    num, 1ULL << 10 * index, 1);
102 		(void) strcat(buf, u);
103 	} else {
104 		(void) mdb_snprintf(buf, MDB_NICENUM_BUFLEN, "%llu%s",
105 		    (u_longlong_t)n, u);
106 	}
107 }
108 
109 ssize_t
110 mdb_vread(void *buf, size_t nbytes, uintptr_t addr)
111 {
112 	ssize_t rbytes = mdb_tgt_vread(mdb.m_target, buf, nbytes, addr);
113 
114 	if (rbytes > 0 && rbytes < nbytes)
115 		return (set_errbytes(rbytes, nbytes));
116 
117 	return (rbytes);
118 }
119 
120 ssize_t
121 mdb_vwrite(const void *buf, size_t nbytes, uintptr_t addr)
122 {
123 	return (mdb_tgt_vwrite(mdb.m_target, buf, nbytes, addr));
124 }
125 
126 ssize_t
127 mdb_aread(void *buf, size_t nbytes, uintptr_t addr, void *as)
128 {
129 	ssize_t rbytes = mdb_tgt_aread(mdb.m_target, as, buf, nbytes, addr);
130 
131 	if (rbytes > 0 && rbytes < nbytes)
132 		return (set_errbytes(rbytes, nbytes));
133 
134 	return (rbytes);
135 }
136 
137 ssize_t
138 mdb_awrite(const void *buf, size_t nbytes, uintptr_t addr, void *as)
139 {
140 	return (mdb_tgt_awrite(mdb.m_target, as, buf, nbytes, addr));
141 }
142 
143 ssize_t
144 mdb_fread(void *buf, size_t nbytes, uintptr_t addr)
145 {
146 	ssize_t rbytes = mdb_tgt_fread(mdb.m_target, buf, nbytes, addr);
147 
148 	if (rbytes > 0 && rbytes < nbytes)
149 		return (set_errbytes(rbytes, nbytes));
150 
151 	return (rbytes);
152 }
153 
154 ssize_t
155 mdb_fwrite(const void *buf, size_t nbytes, uintptr_t addr)
156 {
157 	return (mdb_tgt_fwrite(mdb.m_target, buf, nbytes, addr));
158 }
159 
160 ssize_t
161 mdb_pread(void *buf, size_t nbytes, physaddr_t addr)
162 {
163 	ssize_t rbytes = mdb_tgt_pread(mdb.m_target, buf, nbytes, addr);
164 
165 	if (rbytes > 0 && rbytes < nbytes)
166 		return (set_errbytes(rbytes, nbytes));
167 
168 	return (rbytes);
169 }
170 
171 ssize_t
172 mdb_pwrite(const void *buf, size_t nbytes, physaddr_t addr)
173 {
174 	return (mdb_tgt_pwrite(mdb.m_target, buf, nbytes, addr));
175 }
176 
177 ssize_t
178 mdb_readstr(char *buf, size_t nbytes, uintptr_t addr)
179 {
180 	return (mdb_tgt_readstr(mdb.m_target, MDB_TGT_AS_VIRT,
181 	    buf, nbytes, addr));
182 }
183 
184 ssize_t
185 mdb_writestr(const char *buf, uintptr_t addr)
186 {
187 	return (mdb_tgt_writestr(mdb.m_target, MDB_TGT_AS_VIRT, buf, addr));
188 }
189 
190 ssize_t
191 mdb_readsym(void *buf, size_t nbytes, const char *name)
192 {
193 	ssize_t rbytes = mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT,
194 	    buf, nbytes, MDB_TGT_OBJ_EVERY, name);
195 
196 	if (rbytes > 0 && rbytes < nbytes)
197 		return (set_errbytes(rbytes, nbytes));
198 
199 	return (rbytes);
200 }
201 
202 ssize_t
203 mdb_writesym(const void *buf, size_t nbytes, const char *name)
204 {
205 	return (mdb_tgt_writesym(mdb.m_target, MDB_TGT_AS_VIRT,
206 	    buf, nbytes, MDB_TGT_OBJ_EVERY, name));
207 }
208 
209 ssize_t
210 mdb_readvar(void *buf, const char *name)
211 {
212 	GElf_Sym sym;
213 
214 	if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EVERY,
215 	    name, &sym, NULL))
216 		return (-1);
217 
218 	if (mdb_tgt_vread(mdb.m_target, buf, sym.st_size,
219 	    (uintptr_t)sym.st_value) == sym.st_size)
220 		return ((ssize_t)sym.st_size);
221 
222 	return (-1);
223 }
224 
225 ssize_t
226 mdb_writevar(const void *buf, const char *name)
227 {
228 	GElf_Sym sym;
229 
230 	if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EVERY,
231 	    name, &sym, NULL))
232 		return (-1);
233 
234 	if (mdb_tgt_vwrite(mdb.m_target, buf, sym.st_size,
235 	    (uintptr_t)sym.st_value) == sym.st_size)
236 		return ((ssize_t)sym.st_size);
237 
238 	return (-1);
239 }
240 
241 int
242 mdb_lookup_by_name(const char *name, GElf_Sym *sym)
243 {
244 	return (mdb_lookup_by_obj(MDB_TGT_OBJ_EVERY, name, sym));
245 }
246 
247 int
248 mdb_lookup_by_obj(const char *obj, const char *name, GElf_Sym *sym)
249 {
250 	return (mdb_tgt_lookup_by_name(mdb.m_target, obj, name, sym, NULL));
251 }
252 
253 int
254 mdb_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf,
255     size_t nbytes, GElf_Sym *sym)
256 {
257 	return (mdb_tgt_lookup_by_addr(mdb.m_target, addr, flags,
258 	    buf, nbytes, sym, NULL));
259 }
260 
261 int
262 mdb_getareg(mdb_tid_t tid, const char *rname, mdb_reg_t *rp)
263 {
264 	return (mdb_tgt_getareg(mdb.m_target, tid, rname, rp));
265 }
266 
267 static u_longlong_t
268 mdb_strtoull_int(const char *s, int radix)
269 {
270 	if (s[0] == '0') {
271 		switch (s[1]) {
272 		case 'I':
273 		case 'i':
274 			radix = 2;
275 			s += 2;
276 			break;
277 		case 'O':
278 		case 'o':
279 			radix = 8;
280 			s += 2;
281 			break;
282 		case 'T':
283 		case 't':
284 			radix = 10;
285 			s += 2;
286 			break;
287 		case 'X':
288 		case 'x':
289 			radix = 16;
290 			s += 2;
291 			break;
292 		}
293 	}
294 
295 	return (mdb_strtonum(s, radix));
296 }
297 
298 u_longlong_t
299 mdb_strtoullx(const char *s, mdb_strtoull_flags_t flags)
300 {
301 	int radix;
302 
303 	if ((flags & ~MDB_STRTOULL_F_BASE_C) != 0) {
304 		mdb_warn("invalid options specified: 0x%lx" PRIx64 "\n",
305 		    (uint64_t)flags);
306 		return ((uintmax_t)ULLONG_MAX);
307 	}
308 
309 	if ((flags & MDB_STRTOULL_F_BASE_C) != 0) {
310 		radix = 10;
311 	} else {
312 		radix = mdb.m_radix;
313 	}
314 
315 	return (mdb_strtoull_int(s, radix));
316 }
317 
318 u_longlong_t
319 mdb_strtoull(const char *s)
320 {
321 	return (mdb_strtoull_int(s, mdb.m_radix));
322 }
323 
324 size_t
325 mdb_snprintf(char *buf, size_t nbytes, const char *format, ...)
326 {
327 	va_list alist;
328 
329 	va_start(alist, format);
330 	nbytes = mdb_iob_vsnprintf(buf, nbytes, format, alist);
331 	va_end(alist);
332 
333 	return (nbytes);
334 }
335 
336 void
337 mdb_printf(const char *format, ...)
338 {
339 	va_list alist;
340 
341 	va_start(alist, format);
342 	mdb_iob_vprintf(mdb.m_out, format, alist);
343 	va_end(alist);
344 }
345 
346 void
347 mdb_warn(const char *format, ...)
348 {
349 	va_list alist;
350 
351 	va_start(alist, format);
352 	vwarn(format, alist);
353 	va_end(alist);
354 }
355 
356 void
357 mdb_flush(void)
358 {
359 	mdb_iob_flush(mdb.m_out);
360 }
361 
362 /*
363  * Convert an object of len bytes pointed to by srcraw between
364  * network-order and host-order and store in dstraw.  The length len must
365  * be the actual length of the objects pointed to by srcraw and dstraw (or
366  * zero) or the results are undefined.  srcraw and dstraw may be the same,
367  * in which case the object is converted in-place.  Note that this routine
368  * will convert from host-order to network-order or network-order to
369  * host-order, since the conversion is the same in either case.
370  */
371 /* ARGSUSED */
372 void
373 mdb_nhconvert(void *dstraw, const void *srcraw, size_t len)
374 {
375 #ifdef	_LITTLE_ENDIAN
376 	uint8_t	b1, b2;
377 	uint8_t *dst, *src;
378 	size_t i;
379 
380 	dst = (uint8_t *)dstraw;
381 	src = (uint8_t *)srcraw;
382 	for (i = 0; i < len / 2; i++) {
383 		b1 = src[i];
384 		b2 = src[len - i - 1];
385 		dst[i] = b2;
386 		dst[len - i - 1] = b1;
387 	}
388 #else
389 	if (dstraw != srcraw)
390 		bcopy(srcraw, dstraw, len);
391 #endif
392 }
393 
394 
395 /*
396  * Bit formatting functions: Note the interesting use of UM_GC here to
397  * allocate a buffer for the caller which will be automatically freed
398  * when the dcmd completes or is forcibly aborted.
399  */
400 
401 #define	NBNB			(NBBY / 2)	/* number of bits per nibble */
402 #define	SETBIT(buf, j, c) { \
403 	if (((j) + 1) % (NBNB + 1) == 0) \
404 		(buf)[(j)++] = ' '; \
405 	(buf)[(j)++] = (c); \
406 }
407 
408 const char *
409 mdb_one_bit(int width, int bit, int on)
410 {
411 	int i, j = 0;
412 	char *buf;
413 
414 	buf = mdb_zalloc(width + (width / NBNB) + 2, UM_GC | UM_SLEEP);
415 
416 	for (i = --width; i > bit; i--)
417 		SETBIT(buf, j, '.');
418 
419 	SETBIT(buf, j, on ? '1' : '0');
420 
421 	for (i = bit - 1; i >= 0; i--)
422 		SETBIT(buf, j, '.');
423 
424 	return (buf);
425 }
426 
427 const char *
428 mdb_inval_bits(int width, int start, int stop)
429 {
430 	int i, j = 0;
431 	char *buf;
432 
433 	buf = mdb_zalloc(width + (width / NBNB) + 2, UM_GC | UM_SLEEP);
434 
435 	for (i = --width; i > stop; i--)
436 		SETBIT(buf, j, '.');
437 
438 	for (i = stop; i >= start; i--)
439 		SETBIT(buf, j, 'x');
440 
441 	for (; i >= 0; i--)
442 		SETBIT(buf, j, '.');
443 
444 	return (buf);
445 }
446 
447 ulong_t
448 mdb_inc_indent(ulong_t i)
449 {
450 	if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT) {
451 		ulong_t margin = mdb_iob_getmargin(mdb.m_out);
452 		mdb_iob_margin(mdb.m_out, margin + i);
453 		return (margin);
454 	}
455 
456 	mdb_iob_margin(mdb.m_out, i);
457 	mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
458 	return (0);
459 }
460 
461 ulong_t
462 mdb_dec_indent(ulong_t i)
463 {
464 	if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT) {
465 		ulong_t margin = mdb_iob_getmargin(mdb.m_out);
466 
467 		if (margin < i || margin - i == 0) {
468 			mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
469 			mdb_iob_margin(mdb.m_out, MDB_IOB_DEFMARGIN);
470 		} else
471 			mdb_iob_margin(mdb.m_out, margin - i);
472 
473 		return (margin);
474 	}
475 
476 	return (0);
477 }
478 
479 int
480 mdb_eval(const char *s)
481 {
482 	mdb_frame_t *ofp = mdb.m_fmark;
483 	mdb_frame_t *fp = mdb.m_frame;
484 	int err;
485 
486 	if (s == NULL)
487 		return (set_errno(EINVAL));
488 
489 	/*
490 	 * Push m_in down onto the input stack, then set m_in to point to the
491 	 * i/o buffer for our command string, and reset the frame marker.
492 	 * The mdb_run() function returns when the new m_in iob reaches EOF.
493 	 */
494 	mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
495 	mdb.m_in = mdb_iob_create(mdb_strio_create(s), MDB_IOB_RDONLY);
496 
497 	mdb.m_fmark = NULL;
498 	err = mdb_run();
499 	mdb.m_fmark = ofp;
500 
501 	/*
502 	 * Now pop the old standard input stream and restore mdb.m_in and
503 	 * the parser's saved current line number.
504 	 */
505 	mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
506 	yylineno = mdb_iob_lineno(mdb.m_in);
507 
508 	/*
509 	 * If mdb_run() returned an error, propagate this backward
510 	 * up the stack of debugger environment frames.
511 	 */
512 	if (MDB_ERR_IS_FATAL(err))
513 		longjmp(fp->f_pcb, err);
514 
515 	if (err == MDB_ERR_PAGER || err == MDB_ERR_SIGINT)
516 		return (set_errno(EMDB_CANCEL));
517 
518 	if (err != 0)
519 		return (set_errno(EMDB_EVAL));
520 
521 	return (0);
522 }
523 
524 void
525 mdb_set_dot(uintmax_t addr)
526 {
527 	mdb_nv_set_value(mdb.m_dot, addr);
528 	mdb.m_incr = 0;
529 }
530 
531 uintmax_t
532 mdb_get_dot(void)
533 {
534 	return (mdb_nv_get_value(mdb.m_dot));
535 }
536 
537 static int
538 walk_step(mdb_wcb_t *wcb)
539 {
540 	mdb_wcb_t *nwcb = wcb->w_lyr_head;
541 	int status;
542 
543 	/*
544 	 * If the control block has no layers, we just invoke the walker's
545 	 * step function and return status indicating whether to continue
546 	 * or stop.  If the control block has layers, we need to invoke
547 	 * ourself recursively for the next layer, until eventually we
548 	 * percolate down to an unlayered walk.
549 	 */
550 	if (nwcb == NULL)
551 		return (wcb->w_walker->iwlk_step(&wcb->w_state));
552 
553 	if ((status = walk_step(nwcb)) != WALK_NEXT) {
554 		wcb->w_lyr_head = nwcb->w_lyr_link;
555 		nwcb->w_lyr_link = NULL;
556 		mdb_wcb_destroy(nwcb);
557 	}
558 
559 	if (status == WALK_DONE && wcb->w_lyr_head != NULL)
560 		return (WALK_NEXT);
561 
562 	return (status);
563 }
564 
565 static int
566 walk_common(mdb_wcb_t *wcb)
567 {
568 	int status, rval = 0;
569 	mdb_frame_t *pfp;
570 
571 	/*
572 	 * Enter the control block in the active list so that mdb can clean
573 	 * up after it in case we abort out of the current command.
574 	 */
575 	if ((pfp = mdb_list_prev(mdb.m_frame)) != NULL && pfp->f_pcmd != NULL)
576 		mdb_wcb_insert(wcb, pfp);
577 	else
578 		mdb_wcb_insert(wcb, mdb.m_frame);
579 
580 	/*
581 	 * The per-walk constructor performs private buffer initialization
582 	 * and locates whatever symbols are necessary.
583 	 */
584 	if ((status = wcb->w_walker->iwlk_init(&wcb->w_state)) != WALK_NEXT) {
585 		if (status != WALK_DONE)
586 			rval = set_errno(EMDB_WALKINIT);
587 		goto done;
588 	}
589 
590 	/*
591 	 * Mark wcb to indicate that walk_init has been called (which means
592 	 * we can call walk_fini if the walk is aborted at this point).
593 	 */
594 	wcb->w_inited = TRUE;
595 
596 	while (walk_step(wcb) == WALK_NEXT)
597 		continue;
598 done:
599 	if ((pfp = mdb_list_prev(mdb.m_frame)) != NULL && pfp->f_pcmd != NULL)
600 		mdb_wcb_delete(wcb, pfp);
601 	else
602 		mdb_wcb_delete(wcb, mdb.m_frame);
603 
604 	mdb_wcb_destroy(wcb);
605 	return (rval);
606 }
607 
608 typedef struct pwalk_step {
609 	mdb_walk_cb_t ps_cb;
610 	void *ps_private;
611 } pwalk_step_t;
612 
613 static int
614 pwalk_step(uintptr_t addr, const void *data, void *private)
615 {
616 	pwalk_step_t *psp = private;
617 	int ret;
618 
619 	mdb.m_frame->f_cbactive = B_TRUE;
620 	ret = psp->ps_cb(addr, data, psp->ps_private);
621 	mdb.m_frame->f_cbactive = B_FALSE;
622 
623 	return (ret);
624 }
625 
626 int
627 mdb_pwalk(const char *name, mdb_walk_cb_t func, void *private, uintptr_t addr)
628 {
629 	mdb_iwalker_t *iwp = mdb_walker_lookup(name);
630 	pwalk_step_t p;
631 
632 	if (func == NULL)
633 		return (set_errno(EINVAL));
634 
635 	p.ps_cb = func;
636 	p.ps_private = private;
637 
638 	if (iwp != NULL) {
639 		int ret;
640 		int cbactive = mdb.m_frame->f_cbactive;
641 		mdb.m_frame->f_cbactive = B_FALSE;
642 		ret = walk_common(mdb_wcb_create(iwp, pwalk_step, &p, addr));
643 		mdb.m_frame->f_cbactive = cbactive;
644 		return (ret);
645 	}
646 
647 	return (-1); /* errno is set for us */
648 }
649 
650 int
651 mdb_walk(const char *name, mdb_walk_cb_t func, void *data)
652 {
653 	return (mdb_pwalk(name, func, data, 0));
654 }
655 
656 /*ARGSUSED*/
657 static int
658 walk_dcmd(uintptr_t addr, const void *ignored, dcmd_walk_arg_t *dwp)
659 {
660 	int status;
661 
662 	mdb.m_frame->f_cbactive = B_TRUE;
663 	status = call_idcmd(dwp->dw_dcmd, addr, 1, dwp->dw_flags,
664 	    &dwp->dw_argv);
665 	mdb.m_frame->f_cbactive = B_FALSE;
666 
667 	if (status == DCMD_USAGE || status == DCMD_ABORT)
668 		return (WALK_ERR);
669 
670 	dwp->dw_flags &= ~DCMD_LOOPFIRST;
671 	return (WALK_NEXT);
672 }
673 
674 int
675 mdb_pwalk_dcmd(const char *wname, const char *dcname,
676     int argc, const mdb_arg_t *argv, uintptr_t addr)
677 {
678 	mdb_argvec_t args;
679 	dcmd_walk_arg_t dw;
680 	mdb_iwalker_t *iwp;
681 	mdb_wcb_t *wcb;
682 	int status;
683 
684 	if (wname == NULL || dcname == NULL)
685 		return (set_errno(EINVAL));
686 
687 	if ((dw.dw_dcmd = mdb_dcmd_lookup(dcname)) == NULL)
688 		return (-1); /* errno is set for us */
689 
690 	if ((iwp = mdb_walker_lookup(wname)) == NULL)
691 		return (-1); /* errno is set for us */
692 
693 	args.a_data = (mdb_arg_t *)argv;
694 	args.a_nelems = args.a_size = argc;
695 
696 	mdb_argvec_create(&dw.dw_argv);
697 	mdb_argvec_copy(&dw.dw_argv, &args);
698 	dw.dw_flags = DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC;
699 
700 	wcb = mdb_wcb_create(iwp, (mdb_walk_cb_t)walk_dcmd, &dw, addr);
701 	status = walk_common(wcb);
702 
703 	mdb_argvec_zero(&dw.dw_argv);
704 	mdb_argvec_destroy(&dw.dw_argv);
705 
706 	return (status);
707 }
708 
709 int
710 mdb_walk_dcmd(const char *wname, const char *dcname,
711     int argc, const mdb_arg_t *argv)
712 {
713 	return (mdb_pwalk_dcmd(wname, dcname, argc, argv, 0));
714 }
715 
716 /*ARGSUSED*/
717 static int
718 layered_walk_step(uintptr_t addr, const void *data, mdb_wcb_t *wcb)
719 {
720 	/*
721 	 * Prior to calling the top-level walker's step function, reset its
722 	 * mdb_walk_state_t walk_addr and walk_layer members to refer to the
723 	 * target virtual address and data buffer of the underlying object.
724 	 */
725 	wcb->w_state.walk_addr = addr;
726 	wcb->w_state.walk_layer = data;
727 
728 	return (wcb->w_walker->iwlk_step(&wcb->w_state));
729 }
730 
731 int
732 mdb_layered_walk(const char *wname, mdb_walk_state_t *wsp)
733 {
734 	mdb_wcb_t *cwcb, *wcb;
735 	mdb_iwalker_t *iwp;
736 
737 	if (wname == NULL || wsp == NULL)
738 		return (set_errno(EINVAL));
739 
740 	if ((iwp = mdb_walker_lookup(wname)) == NULL)
741 		return (-1); /* errno is set for us */
742 
743 	if ((cwcb = mdb_wcb_from_state(wsp)) == NULL)
744 		return (set_errno(EMDB_BADWCB));
745 
746 	if (cwcb->w_walker == iwp)
747 		return (set_errno(EMDB_WALKLOOP));
748 
749 	wcb = mdb_wcb_create(iwp, (mdb_walk_cb_t)layered_walk_step,
750 	    cwcb, wsp->walk_addr);
751 
752 	if (iwp->iwlk_init(&wcb->w_state) != WALK_NEXT) {
753 		mdb_wcb_destroy(wcb);
754 		return (set_errno(EMDB_WALKINIT));
755 	}
756 
757 	wcb->w_inited = TRUE;
758 
759 	mdb_dprintf(MDB_DBG_WALK, "added %s`%s as %s`%s layer\n",
760 	    iwp->iwlk_modp->mod_name, iwp->iwlk_name,
761 	    cwcb->w_walker->iwlk_modp->mod_name, cwcb->w_walker->iwlk_name);
762 
763 	if (cwcb->w_lyr_head != NULL) {
764 		for (cwcb = cwcb->w_lyr_head; cwcb->w_lyr_link != NULL; )
765 			cwcb = cwcb->w_lyr_link;
766 		cwcb->w_lyr_link = wcb;
767 	} else
768 		cwcb->w_lyr_head = wcb;
769 
770 	return (0);
771 }
772 
773 int
774 mdb_call_dcmd(const char *name, uintptr_t dot, uint_t flags,
775     int argc, const mdb_arg_t *argv)
776 {
777 	mdb_idcmd_t *idcp;
778 	mdb_argvec_t args;
779 	int status;
780 
781 	if (name == NULL || argc < 0)
782 		return (set_errno(EINVAL));
783 
784 	if ((idcp = mdb_dcmd_lookup(name)) == NULL)
785 		return (-1); /* errno is set for us */
786 
787 	args.a_data = (mdb_arg_t *)argv;
788 	args.a_nelems = args.a_size = argc;
789 	status = call_idcmd(idcp, dot, 1, flags, &args);
790 
791 	if (status == DCMD_ERR || status == DCMD_ABORT)
792 		return (set_errno(EMDB_DCFAIL));
793 
794 	if (status == DCMD_USAGE)
795 		return (set_errno(EMDB_DCUSAGE));
796 
797 	return (0);
798 }
799 
800 /*
801  * When dcmds or walkers call a dcmd that might be in another module,
802  * we need to set mdb.m_frame->f_cp to an mdb_cmd that represents the
803  * dcmd we're currently executing, otherwise mdb_get_module gets the
804  * module of the caller instead of the module for the current dcmd.
805  */
806 static int
807 call_idcmd(mdb_idcmd_t *idcp, uintmax_t addr, uintmax_t count,
808     uint_t flags, mdb_argvec_t *argv)
809 {
810 	mdb_cmd_t *save_cp;
811 	mdb_cmd_t cmd;
812 	int ret;
813 
814 	bzero(&cmd, sizeof (cmd));
815 	cmd.c_dcmd = idcp;
816 	cmd.c_argv = *argv;
817 
818 	save_cp = mdb.m_frame->f_cp;
819 	mdb.m_frame->f_cp = &cmd;
820 
821 	ret = mdb_call_idcmd(cmd.c_dcmd, addr, count, flags,
822 	    &cmd.c_argv, NULL, NULL);
823 
824 	mdb.m_frame->f_cp = save_cp;
825 
826 	return (ret);
827 }
828 
829 int
830 mdb_add_walker(const mdb_walker_t *wp)
831 {
832 	mdb_module_t *mp;
833 
834 	if (mdb.m_lmod == NULL) {
835 		mdb_cmd_t *cp = mdb.m_frame->f_cp;
836 		mp = cp->c_dcmd->idc_modp;
837 	} else
838 		mp = mdb.m_lmod;
839 
840 	return (mdb_module_add_walker(mp, wp, 0));
841 }
842 
843 int
844 mdb_remove_walker(const char *name)
845 {
846 	mdb_module_t *mp;
847 
848 	if (mdb.m_lmod == NULL) {
849 		mdb_cmd_t *cp = mdb.m_frame->f_cp;
850 		mp = cp->c_dcmd->idc_modp;
851 	} else
852 		mp = mdb.m_lmod;
853 
854 	return (mdb_module_remove_walker(mp, name));
855 }
856 
857 void
858 mdb_get_pipe(mdb_pipe_t *p)
859 {
860 	mdb_cmd_t *cp = mdb.m_frame->f_cp;
861 	mdb_addrvec_t *adp = &cp->c_addrv;
862 
863 	if (p == NULL) {
864 		warn("dcmd failure: mdb_get_pipe invoked with NULL pointer\n");
865 		longjmp(mdb.m_frame->f_pcb, MDB_ERR_API);
866 	}
867 
868 	if (adp->ad_nelems != 0) {
869 		ASSERT(adp->ad_ndx != 0);
870 		p->pipe_data = &adp->ad_data[adp->ad_ndx - 1];
871 		p->pipe_len = adp->ad_nelems - adp->ad_ndx + 1;
872 		adp->ad_ndx = adp->ad_nelems;
873 	} else {
874 		p->pipe_data = NULL;
875 		p->pipe_len = 0;
876 	}
877 }
878 
879 void
880 mdb_set_pipe(const mdb_pipe_t *p)
881 {
882 	mdb_cmd_t *cp = mdb.m_frame->f_pcmd;
883 
884 	if (p == NULL) {
885 		warn("dcmd failure: mdb_set_pipe invoked with NULL pointer\n");
886 		longjmp(mdb.m_frame->f_pcb, MDB_ERR_API);
887 	}
888 
889 	if (cp != NULL) {
890 		size_t nbytes = sizeof (uintptr_t) * p->pipe_len;
891 
892 		mdb_cmd_reset(cp);
893 		cp->c_addrv.ad_data = mdb_alloc(nbytes, UM_SLEEP);
894 		bcopy(p->pipe_data, cp->c_addrv.ad_data, nbytes);
895 		cp->c_addrv.ad_nelems = p->pipe_len;
896 		cp->c_addrv.ad_size = p->pipe_len;
897 	}
898 }
899 
900 ssize_t
901 mdb_get_xdata(const char *name, void *buf, size_t nbytes)
902 {
903 	return (mdb_tgt_getxdata(mdb.m_target, name, buf, nbytes));
904 }
905 
906 /*
907  * Private callback structure for implementing mdb_object_iter, below.
908  */
909 typedef struct {
910 	mdb_object_cb_t oi_cb;
911 	void *oi_arg;
912 	int oi_rval;
913 } object_iter_arg_t;
914 
915 /*ARGSUSED*/
916 static int
917 mdb_object_cb(void *data, const mdb_map_t *map, const char *fullname)
918 {
919 	object_iter_arg_t *arg = data;
920 	mdb_object_t obj;
921 
922 	if (arg->oi_rval != 0)
923 		return (0);
924 
925 	bzero(&obj, sizeof (obj));
926 	obj.obj_base = map->map_base;
927 	obj.obj_name = strbasename(map->map_name);
928 	obj.obj_size = map->map_size;
929 	obj.obj_fullname = fullname;
930 
931 	arg->oi_rval = arg->oi_cb(&obj, arg->oi_arg);
932 
933 	return (0);
934 }
935 
936 int
937 mdb_object_iter(mdb_object_cb_t cb, void *data)
938 {
939 	object_iter_arg_t arg;
940 
941 	arg.oi_cb = cb;
942 	arg.oi_arg = data;
943 	arg.oi_rval = 0;
944 
945 	if (mdb_tgt_object_iter(mdb.m_target, mdb_object_cb, &arg) != 0)
946 		return (-1);
947 
948 	return (arg.oi_rval);
949 }
950 
951 /*
952  * Private callback structure for implementing mdb_symbol_iter, below.
953  */
954 typedef struct {
955 	mdb_symbol_cb_t si_cb;
956 	void *si_arg;
957 	int si_rval;
958 } symbol_iter_arg_t;
959 
960 /*ARGSUSED*/
961 static int
962 mdb_symbol_cb(void *data, const GElf_Sym *gsym, const char *name,
963     const mdb_syminfo_t *sip, const char *obj)
964 {
965 	symbol_iter_arg_t *arg = data;
966 	mdb_symbol_t sym;
967 
968 	if (arg->si_rval != 0)
969 		return (0);
970 
971 	bzero(&sym, sizeof (sym));
972 	sym.sym_name = name;
973 	sym.sym_object = obj;
974 	sym.sym_sym = gsym;
975 	sym.sym_table = sip->sym_table;
976 	sym.sym_id = sip->sym_id;
977 
978 	arg->si_rval = arg->si_cb(&sym, arg->si_arg);
979 
980 	return (0);
981 }
982 
983 int
984 mdb_symbol_iter(const char *obj, uint_t which, uint_t type,
985     mdb_symbol_cb_t cb, void *data)
986 {
987 	symbol_iter_arg_t arg;
988 
989 	arg.si_cb = cb;
990 	arg.si_arg = data;
991 	arg.si_rval = 0;
992 
993 	if (mdb_tgt_symbol_iter(mdb.m_target, obj, which, type,
994 	    mdb_symbol_cb, &arg) != 0)
995 		return (-1);
996 
997 	return (arg.si_rval);
998 }
999 
1000 /*
1001  * Private structure and function for implementing mdb_dumpptr on top
1002  * of mdb_dump_internal
1003  */
1004 typedef struct dptrdat {
1005 	mdb_dumpptr_cb_t func;
1006 	void *arg;
1007 } dptrdat_t;
1008 
1009 static ssize_t
1010 mdb_dump_aux_ptr(void *buf, size_t nbyte, uint64_t offset, void *arg)
1011 {
1012 	dptrdat_t *dat = arg;
1013 
1014 	return (dat->func(buf, nbyte, offset, dat->arg));
1015 }
1016 
1017 /*
1018  * Private structure and function for handling callbacks which return
1019  * EMDB_PARTIAL
1020  */
1021 typedef struct d64dat {
1022 	mdb_dump64_cb_t func;
1023 	void *arg;
1024 } d64dat_t;
1025 
1026 static ssize_t
1027 mdb_dump_aux_partial(void *buf, size_t nbyte, uint64_t offset, void *arg)
1028 {
1029 	d64dat_t *dat = arg;
1030 	int result;
1031 	int count;
1032 
1033 	result = dat->func(buf, nbyte, offset, dat->arg);
1034 	if (result == -1 && errno == EMDB_PARTIAL) {
1035 		count = 0;
1036 		do {
1037 			result = dat->func((char *)buf + count, 1,
1038 			    offset + count, dat->arg);
1039 			if (result == 1)
1040 				count++;
1041 		} while (count < nbyte && result == 1);
1042 		if (count)
1043 			result = count;
1044 	}
1045 
1046 	return (result);
1047 }
1048 
1049 /* Default callback for mdb_dumpptr() is calling mdb_vread(). */
1050 static ssize_t
1051 mdb_dumpptr_cb(void *buf, size_t nbytes, uintptr_t addr, void *arg __unused)
1052 {
1053 	return (mdb_vread(buf, nbytes, addr));
1054 }
1055 
1056 int
1057 mdb_dumpptr(uintptr_t addr, size_t len, uint_t flags, mdb_dumpptr_cb_t fp,
1058     void *arg)
1059 {
1060 	dptrdat_t dat;
1061 	d64dat_t dat64;
1062 
1063 	if (fp == NULL)
1064 		dat.func = mdb_dumpptr_cb;
1065 	else
1066 		dat.func = fp;
1067 	dat.arg = arg;
1068 	dat64.func = mdb_dump_aux_ptr;
1069 	dat64.arg = &dat;
1070 	return (mdb_dump_internal(addr, len, flags, mdb_dump_aux_partial,
1071 	    &dat64, sizeof (uintptr_t)));
1072 }
1073 
1074 int
1075 mdb_dump64(uint64_t addr, uint64_t len, uint_t flags, mdb_dump64_cb_t fp,
1076     void *arg)
1077 {
1078 	d64dat_t dat64;
1079 
1080 	dat64.func = fp;
1081 	dat64.arg = arg;
1082 	return (mdb_dump_internal(addr, len, flags, mdb_dump_aux_partial,
1083 	    &dat64, sizeof (uint64_t)));
1084 }
1085 
1086 int
1087 mdb_get_state(void)
1088 {
1089 	mdb_tgt_status_t ts;
1090 
1091 	(void) mdb_tgt_status(mdb.m_target, &ts);
1092 
1093 	return (ts.st_state);
1094 }
1095 
1096 void *
1097 mdb_callback_add(int class, mdb_callback_f fp, void *arg)
1098 {
1099 	mdb_module_t *m;
1100 
1101 	if (class != MDB_CALLBACK_STCHG && class != MDB_CALLBACK_PROMPT) {
1102 		(void) set_errno(EINVAL);
1103 		return (NULL);
1104 	}
1105 
1106 	if (mdb.m_lmod != NULL)
1107 		m = mdb.m_lmod;
1108 	else
1109 		m = mdb.m_frame->f_cp->c_dcmd->idc_modp;
1110 
1111 	return (mdb_callb_add(m, class, fp, arg));
1112 }
1113 
1114 void
1115 mdb_callback_remove(void *hdl)
1116 {
1117 	mdb_callb_remove(hdl);
1118 }
1119