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 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26
27 /*
28 * Copyright (c) 2012 by Delphix. All rights reserved.
29 * Copyright 2021 Joyent, Inc.
30 * Copyright (c) 2013 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
31 * Copyright (c) 2015, 2017 by Delphix. All rights reserved.
32 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
33 * Copyright 2025 Oxide Computer Company
34 * Copyright 2025 Edgecast Cloud LLC
35 */
36
37 #include <sys/elf.h>
38 #include <sys/elf_SPARC.h>
39
40 #include <libproc.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <alloca.h>
46 #include <libctf.h>
47 #include <ctype.h>
48
49 #include <mdb/mdb_string.h>
50 #include <mdb/mdb_argvec.h>
51 #include <mdb/mdb_nv.h>
52 #include <mdb/mdb_fmt.h>
53 #include <mdb/mdb_target.h>
54 #include <mdb/mdb_err.h>
55 #include <mdb/mdb_debug.h>
56 #include <mdb/mdb_conf.h>
57 #include <mdb/mdb_module.h>
58 #include <mdb/mdb_modapi.h>
59 #include <mdb/mdb_stdlib.h>
60 #include <mdb/mdb_lex.h>
61 #include <mdb/mdb_io_impl.h>
62 #include <mdb/mdb_help.h>
63 #include <mdb/mdb_disasm.h>
64 #include <mdb/mdb_frame.h>
65 #include <mdb/mdb_evset.h>
66 #include <mdb/mdb_print.h>
67 #include <mdb/mdb_nm.h>
68 #include <mdb/mdb_set.h>
69 #include <mdb/mdb_demangle.h>
70 #include <mdb/mdb_ctf.h>
71 #include <mdb/mdb_whatis.h>
72 #include <mdb/mdb_whatis_impl.h>
73 #include <mdb/mdb_macalias.h>
74 #include <mdb/mdb_tab.h>
75 #include <mdb/mdb_typedef.h>
76 #include <mdb/mdb_linkerset.h>
77 #ifdef _KMDB
78 #include <kmdb/kmdb_kdi.h>
79 #endif
80 #include <mdb/mdb.h>
81
82 #ifdef __sparc
83 #define SETHI_MASK 0xc1c00000
84 #define SETHI_VALUE 0x01000000
85
86 #define IS_SETHI(machcode) (((machcode) & SETHI_MASK) == SETHI_VALUE)
87
88 #define OP(machcode) ((machcode) >> 30)
89 #define OP3(machcode) (((machcode) >> 19) & 0x3f)
90 #define RD(machcode) (((machcode) >> 25) & 0x1f)
91 #define RS1(machcode) (((machcode) >> 14) & 0x1f)
92 #define I(machcode) (((machcode) >> 13) & 0x01)
93
94 #define IMM13(machcode) ((machcode) & 0x1fff)
95 #define IMM22(machcode) ((machcode) & 0x3fffff)
96
97 #define OP_ARITH_MEM_MASK 0x2
98 #define OP_ARITH 0x2
99 #define OP_MEM 0x3
100
101 #define OP3_CC_MASK 0x10
102 #define OP3_COMPLEX_MASK 0x20
103
104 #define OP3_ADD 0x00
105 #define OP3_OR 0x02
106 #define OP3_XOR 0x03
107
108 #ifndef R_O7
109 #define R_O7 0xf
110 #endif
111 #endif /* __sparc */
112
113 static mdb_tgt_addr_t
write_uint8(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t ull,uint_t rdback)114 write_uint8(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
115 {
116 uint8_t o, n = (uint8_t)ull;
117
118 if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
119 addr) == -1)
120 return (addr);
121
122 if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
123 return (addr);
124
125 if (rdback) {
126 if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
127 return (addr);
128
129 mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8x=%8T0x%x\n",
130 mdb_iob_getmargin(mdb.m_out), addr, o, n);
131 }
132
133 return (addr + sizeof (n));
134 }
135
136 static mdb_tgt_addr_t
write_uint16(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t ull,uint_t rdback)137 write_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
138 {
139 uint16_t o, n = (uint16_t)ull;
140
141 if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
142 addr) == -1)
143 return (addr);
144
145 if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
146 return (addr);
147
148 if (rdback) {
149 if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
150 return (addr);
151
152 mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8hx=%8T0x%hx\n",
153 mdb_iob_getmargin(mdb.m_out), addr, o, n);
154 }
155
156 return (addr + sizeof (n));
157 }
158
159 static mdb_tgt_addr_t
write_uint32(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t ull,uint_t rdback)160 write_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
161 {
162 uint32_t o, n = (uint32_t)ull;
163
164 if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
165 addr) == -1)
166 return (addr);
167
168 if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
169 return (addr);
170
171 if (rdback) {
172 if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
173 return (addr);
174
175 mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#16x=%8T0x%x\n",
176 mdb_iob_getmargin(mdb.m_out), addr, o, n);
177 }
178
179 return (addr + sizeof (n));
180 }
181
182 static mdb_tgt_addr_t
write_uint64(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t n,uint_t rdback)183 write_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
184 {
185 uint64_t o;
186
187 if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
188 addr) == -1)
189 return (addr);
190
191 if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
192 return (addr);
193
194 if (rdback) {
195 if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
196 return (addr);
197
198 mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#24llx=%8T0x%llx\n",
199 mdb_iob_getmargin(mdb.m_out), addr, o, n);
200 }
201
202 return (addr + sizeof (n));
203 }
204
205 /*
206 * Writes to objects of size 1, 2, 4, or 8 bytes. The function
207 * doesn't care if the object is a number or not (e.g. it could
208 * be a byte array, or a struct) as long as the size of the write
209 * is one of the aforementioned ones.
210 */
211 static mdb_tgt_addr_t
write_var_uint(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t val,size_t size,uint_t rdback)212 write_var_uint(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, size_t size,
213 uint_t rdback)
214 {
215 if (size < sizeof (uint64_t)) {
216 uint64_t max_num = 1ULL << (size * NBBY);
217
218 if (val >= max_num) {
219 uint64_t write_len = 0;
220
221 /* count bytes needed for val */
222 while (val != 0) {
223 write_len++;
224 val >>= NBBY;
225 }
226
227 mdb_warn("value too big for the length of the write: "
228 "supplied %llu bytes but maximum is %llu bytes\n",
229 (u_longlong_t)write_len, (u_longlong_t)size);
230 return (addr);
231 }
232 }
233
234 switch (size) {
235 case 1:
236 return (write_uint8(as, addr, val, rdback));
237 case 2:
238 return (write_uint16(as, addr, val, rdback));
239 case 4:
240 return (write_uint32(as, addr, val, rdback));
241 case 8:
242 return (write_uint64(as, addr, val, rdback));
243 default:
244 mdb_warn("writes of size %u are not supported\n ", size);
245 return (addr);
246 }
247 }
248
249 static mdb_tgt_addr_t
write_ctf_uint(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t n,uint_t rdback)250 write_ctf_uint(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
251 {
252 mdb_ctf_id_t mid;
253 size_t size;
254 ssize_t type_size;
255 int kind;
256
257 if (mdb_ctf_lookup_by_addr(addr, &mid) != 0) {
258 mdb_warn("no CTF data found at this address\n");
259 return (addr);
260 }
261
262 kind = mdb_ctf_type_kind(mid);
263 if (kind == CTF_ERR) {
264 mdb_warn("CTF data found but type kind could not be read");
265 return (addr);
266 }
267
268 if (kind == CTF_K_TYPEDEF) {
269 mdb_ctf_id_t temp_id;
270 if (mdb_ctf_type_resolve(mid, &temp_id) != 0) {
271 mdb_warn("failed to resolve type");
272 return (addr);
273 }
274 kind = mdb_ctf_type_kind(temp_id);
275 }
276
277 if (kind != CTF_K_INTEGER && kind != CTF_K_POINTER &&
278 kind != CTF_K_ENUM) {
279 mdb_warn("CTF type should be integer, pointer, or enum\n");
280 return (addr);
281 }
282
283 type_size = mdb_ctf_type_size(mid);
284 if (type_size < 0) {
285 mdb_warn("CTF data found but size could not be read");
286 return (addr);
287 }
288 size = type_size;
289
290 return (write_var_uint(as, addr, n, size, rdback));
291 }
292
293 static int
write_arglist(mdb_tgt_as_t as,mdb_tgt_addr_t addr,int argc,const mdb_arg_t * argv)294 write_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr,
295 int argc, const mdb_arg_t *argv)
296 {
297 mdb_tgt_addr_t (*write_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
298 uint64_t, uint_t);
299 mdb_tgt_addr_t naddr;
300 uintmax_t value;
301 int rdback = mdb.m_flags & MDB_FL_READBACK;
302 size_t i;
303
304 if (argc == 1) {
305 mdb_warn("expected value to write following %c\n",
306 argv->a_un.a_char);
307 return (DCMD_ERR);
308 }
309
310 switch (argv->a_un.a_char) {
311 case 'v':
312 write_value = write_uint8;
313 break;
314 case 'w':
315 write_value = write_uint16;
316 break;
317 case 'z':
318 write_value = write_ctf_uint;
319 break;
320 case 'W':
321 write_value = write_uint32;
322 break;
323 case 'Z':
324 write_value = write_uint64;
325 break;
326 default:
327 write_value = NULL;
328 break;
329 }
330
331 for (argv++, i = 1; i < argc; i++, argv++) {
332 if (argv->a_type == MDB_TYPE_CHAR) {
333 mdb_warn("expected immediate value instead of '%c'\n",
334 argv->a_un.a_char);
335 return (DCMD_ERR);
336 }
337
338 if (argv->a_type == MDB_TYPE_STRING) {
339 if (mdb_eval(argv->a_un.a_str) == -1) {
340 mdb_warn("failed to write \"%s\"",
341 argv->a_un.a_str);
342 return (DCMD_ERR);
343 }
344 value = mdb_nv_get_value(mdb.m_dot);
345 } else
346 value = argv->a_un.a_val;
347
348 mdb_nv_set_value(mdb.m_dot, addr);
349
350 if ((naddr = write_value(as, addr, value, rdback)) == addr) {
351 mdb_warn("failed to write %llr at address 0x%llx",
352 value, addr);
353 mdb.m_incr = 0;
354 return (DCMD_ERR);
355 }
356
357 mdb.m_incr = naddr - addr;
358 addr = naddr;
359 }
360
361 return (DCMD_OK);
362 }
363
364 static mdb_tgt_addr_t
match_uint16(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t v64,uint64_t m64)365 match_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
366 {
367 uint16_t x, val = (uint16_t)v64, mask = (uint16_t)m64;
368
369 for (; mdb_tgt_aread(mdb.m_target, as, &x,
370 sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
371
372 if ((x & mask) == val) {
373 mdb_iob_printf(mdb.m_out, "%lla\n", addr);
374 break;
375 }
376 }
377 return (addr);
378 }
379
380 static mdb_tgt_addr_t
match_uint32(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t v64,uint64_t m64)381 match_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
382 {
383 uint32_t x, val = (uint32_t)v64, mask = (uint32_t)m64;
384
385 for (; mdb_tgt_aread(mdb.m_target, as, &x,
386 sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
387
388 if ((x & mask) == val) {
389 mdb_iob_printf(mdb.m_out, "%lla\n", addr);
390 break;
391 }
392 }
393 return (addr);
394 }
395
396 static mdb_tgt_addr_t
match_uint64(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint64_t val,uint64_t mask)397 match_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, uint64_t mask)
398 {
399 uint64_t x;
400
401 for (; mdb_tgt_aread(mdb.m_target, as, &x,
402 sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
403
404 if ((x & mask) == val) {
405 mdb_iob_printf(mdb.m_out, "%lla\n", addr);
406 break;
407 }
408 }
409 return (addr);
410 }
411
412 static int
match_arglist(mdb_tgt_as_t as,uint_t flags,mdb_tgt_addr_t addr,int argc,const mdb_arg_t * argv)413 match_arglist(mdb_tgt_as_t as, uint_t flags, mdb_tgt_addr_t addr,
414 int argc, const mdb_arg_t *argv)
415 {
416 mdb_tgt_addr_t (*match_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
417 uint64_t, uint64_t);
418
419 uint64_t args[2] = { 0, -1ULL }; /* [ value, mask ] */
420 size_t i;
421
422 if (argc < 2) {
423 mdb_warn("expected value following %c\n", argv->a_un.a_char);
424 return (DCMD_ERR);
425 }
426
427 if (argc > 3) {
428 mdb_warn("only value and mask may follow %c\n",
429 argv->a_un.a_char);
430 return (DCMD_ERR);
431 }
432
433 switch (argv->a_un.a_char) {
434 case 'l':
435 match_value = match_uint16;
436 break;
437 case 'L':
438 match_value = match_uint32;
439 break;
440 case 'M':
441 match_value = match_uint64;
442 break;
443 default:
444 mdb_warn("unknown match value %c\n",
445 argv->a_un.a_char);
446 return (DCMD_ERR);
447 }
448
449 for (argv++, i = 1; i < argc; i++, argv++) {
450 if (argv->a_type == MDB_TYPE_CHAR) {
451 mdb_warn("expected immediate value instead of '%c'\n",
452 argv->a_un.a_char);
453 return (DCMD_ERR);
454 }
455
456 if (argv->a_type == MDB_TYPE_STRING) {
457 if (mdb_eval(argv->a_un.a_str) == -1) {
458 mdb_warn("failed to evaluate \"%s\"",
459 argv->a_un.a_str);
460 return (DCMD_ERR);
461 }
462 args[i - 1] = mdb_nv_get_value(mdb.m_dot);
463 } else
464 args[i - 1] = argv->a_un.a_val;
465 }
466
467 addr = match_value(as, addr, args[0], args[1]);
468 mdb_nv_set_value(mdb.m_dot, addr);
469
470 /*
471 * In adb(1), the match operators ignore any repeat count that has
472 * been applied to them. We emulate this undocumented property
473 * by returning DCMD_ABORT if our input is not a pipeline.
474 */
475 return ((flags & DCMD_PIPE) ? DCMD_OK : DCMD_ABORT);
476 }
477
478 static int
argncmp(int argc,const mdb_arg_t * argv,const char * s)479 argncmp(int argc, const mdb_arg_t *argv, const char *s)
480 {
481 for (; *s != '\0'; s++, argc--, argv++) {
482 if (argc == 0 || argv->a_type != MDB_TYPE_CHAR)
483 return (FALSE);
484 if (argv->a_un.a_char != *s)
485 return (FALSE);
486 }
487 return (TRUE);
488 }
489
490 static int
print_arglist(mdb_tgt_as_t as,mdb_tgt_addr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)491 print_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint_t flags,
492 int argc, const mdb_arg_t *argv)
493 {
494 char buf[MDB_TGT_SYM_NAMLEN];
495 mdb_tgt_addr_t oaddr = addr;
496 mdb_tgt_addr_t naddr;
497 GElf_Sym sym;
498 size_t i, n;
499
500 if (DCMD_HDRSPEC(flags) && (flags & DCMD_PIPE_OUT) == 0) {
501 const char *fmt;
502 int is_dis;
503 /*
504 * This is nasty, but necessary for precise adb compatibility.
505 * Detect disassembly format by looking for "ai" or "ia":
506 */
507 if (argncmp(argc, argv, "ai")) {
508 fmt = "%-#*lla\n";
509 is_dis = TRUE;
510 } else if (argncmp(argc, argv, "ia")) {
511 fmt = "%-#*lla";
512 is_dis = TRUE;
513 } else {
514 fmt = "%-#*lla%16T";
515 is_dis = FALSE;
516 }
517
518 /*
519 * If symbolic decoding is on, disassembly is off, and the
520 * address exactly matches a symbol, print the symbol name:
521 */
522 if ((mdb.m_flags & MDB_FL_PSYM) && !is_dis &&
523 (as == MDB_TGT_AS_VIRT || as == MDB_TGT_AS_FILE) &&
524 mdb_tgt_lookup_by_addr(mdb.m_target, (uintptr_t)addr,
525 MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0)
526 mdb_iob_printf(mdb.m_out, "%s:\n", buf);
527
528 /*
529 * If this is a virtual address, cast it so that it reflects
530 * only the valid component of the address.
531 */
532 if (as == MDB_TGT_AS_VIRT)
533 addr = (uintptr_t)addr;
534
535 mdb_iob_printf(mdb.m_out, fmt,
536 (uint_t)mdb_iob_getmargin(mdb.m_out), addr);
537 }
538
539 if (argc == 0) {
540 /*
541 * Yes, for you trivia buffs: if you use a format verb and give
542 * no format string, you get: X^"= "i ... note that in adb the
543 * the '=' verb once had 'z' as its default, but then 'z' was
544 * deleted (it was once an alias for 'i') and so =\n now calls
545 * scanform("z") and produces a 'bad modifier' message.
546 */
547 static const mdb_arg_t def_argv[] = {
548 { MDB_TYPE_CHAR, MDB_INIT_CHAR('X') },
549 { MDB_TYPE_CHAR, MDB_INIT_CHAR('^') },
550 { MDB_TYPE_STRING, MDB_INIT_STRING("= ") },
551 { MDB_TYPE_CHAR, MDB_INIT_CHAR('i') }
552 };
553
554 argc = sizeof (def_argv) / sizeof (mdb_arg_t);
555 argv = def_argv;
556 }
557
558 mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
559
560 for (i = 0, n = 1; i < argc; i++, argv++) {
561 switch (argv->a_type) {
562 case MDB_TYPE_CHAR:
563 naddr = mdb_fmt_print(mdb.m_target, as, addr, n,
564 argv->a_un.a_char);
565 mdb.m_incr = naddr - addr;
566 addr = naddr;
567 n = 1;
568 break;
569
570 case MDB_TYPE_IMMEDIATE:
571 n = argv->a_un.a_val;
572 break;
573
574 case MDB_TYPE_STRING:
575 mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
576 n = 1;
577 break;
578 }
579 }
580
581 mdb.m_incr = addr - oaddr;
582 mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
583 return (DCMD_OK);
584 }
585
586 static int
print_common(mdb_tgt_as_t as,uint_t flags,int argc,const mdb_arg_t * argv)587 print_common(mdb_tgt_as_t as, uint_t flags, int argc, const mdb_arg_t *argv)
588 {
589 mdb_tgt_addr_t addr = mdb_nv_get_value(mdb.m_dot);
590
591 if (argc != 0 && argv->a_type == MDB_TYPE_CHAR) {
592 if (strchr("vwzWZ", argv->a_un.a_char))
593 return (write_arglist(as, addr, argc, argv));
594 if (strchr("lLM", argv->a_un.a_char))
595 return (match_arglist(as, flags, addr, argc, argv));
596 }
597
598 return (print_arglist(as, addr, flags, argc, argv));
599 }
600
601 /*ARGSUSED*/
602 static int
cmd_print_core(uintptr_t x,uint_t flags,int argc,const mdb_arg_t * argv)603 cmd_print_core(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
604 {
605 return (print_common(MDB_TGT_AS_VIRT, flags, argc, argv));
606 }
607
608 #ifndef _KMDB
609 /*ARGSUSED*/
610 static int
cmd_print_object(uintptr_t x,uint_t flags,int argc,const mdb_arg_t * argv)611 cmd_print_object(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
612 {
613 return (print_common(MDB_TGT_AS_FILE, flags, argc, argv));
614 }
615 #endif
616
617 /*ARGSUSED*/
618 static int
cmd_print_phys(uintptr_t x,uint_t flags,int argc,const mdb_arg_t * argv)619 cmd_print_phys(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
620 {
621 return (print_common(MDB_TGT_AS_PHYS, flags, argc, argv));
622 }
623
624 /*ARGSUSED*/
625 static int
cmd_print_value(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)626 cmd_print_value(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
627 {
628 uintmax_t ndot, dot = mdb_get_dot();
629 const char *tgt_argv[1];
630 mdb_tgt_t *t;
631 size_t i, n;
632
633 if (argc == 0) {
634 mdb_warn("expected one or more format characters "
635 "following '='\n");
636 return (DCMD_ERR);
637 }
638
639 tgt_argv[0] = (const char *)˙
640 t = mdb_tgt_create(mdb_value_tgt_create, 0, 1, tgt_argv);
641 mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
642
643 for (i = 0, n = 1; i < argc; i++, argv++) {
644 switch (argv->a_type) {
645 case MDB_TYPE_CHAR:
646 ndot = mdb_fmt_print(t, MDB_TGT_AS_VIRT,
647 dot, n, argv->a_un.a_char);
648 if (argv->a_un.a_char == '+' ||
649 argv->a_un.a_char == '-')
650 dot = ndot;
651 n = 1;
652 break;
653
654 case MDB_TYPE_IMMEDIATE:
655 n = argv->a_un.a_val;
656 break;
657
658 case MDB_TYPE_STRING:
659 mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
660 n = 1;
661 break;
662 }
663 }
664
665 mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
666 mdb_nv_set_value(mdb.m_dot, dot);
667 mdb.m_incr = 0;
668
669 mdb_tgt_destroy(t);
670 return (DCMD_OK);
671 }
672
673 /*ARGSUSED*/
674 static int
cmd_assign_variable(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)675 cmd_assign_variable(uintptr_t addr, uint_t flags,
676 int argc, const mdb_arg_t *argv)
677 {
678 uintmax_t dot = mdb_nv_get_value(mdb.m_dot);
679 const char *p;
680 mdb_var_t *v;
681
682 if (argc == 2) {
683 if (argv->a_type != MDB_TYPE_CHAR) {
684 mdb_warn("improper arguments following '>' operator\n");
685 return (DCMD_ERR);
686 }
687
688 switch (argv->a_un.a_char) {
689 case 'c':
690 addr = *((uchar_t *)&addr);
691 break;
692 case 's':
693 addr = *((ushort_t *)&addr);
694 break;
695 case 'i':
696 addr = *((uint_t *)&addr);
697 break;
698 case 'l':
699 addr = *((ulong_t *)&addr);
700 break;
701 default:
702 mdb_warn("%c is not a valid // modifier\n",
703 argv->a_un.a_char);
704 return (DCMD_ERR);
705 }
706
707 dot = addr;
708 argv++;
709 argc--;
710 }
711
712 if (argc != 1 || argv->a_type != MDB_TYPE_STRING) {
713 mdb_warn("expected single variable name following '>'\n");
714 return (DCMD_ERR);
715 }
716
717 if (strlen(argv->a_un.a_str) >= (size_t)MDB_NV_NAMELEN) {
718 mdb_warn("variable names may not exceed %d characters\n",
719 MDB_NV_NAMELEN - 1);
720 return (DCMD_ERR);
721 }
722
723 if ((p = strbadid(argv->a_un.a_str)) != NULL) {
724 mdb_warn("'%c' may not be used in a variable name\n", *p);
725 return (DCMD_ERR);
726 }
727
728 if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
729 (void) mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str, NULL, dot, 0);
730 else
731 mdb_nv_set_value(v, dot);
732
733 mdb.m_incr = 0;
734 return (DCMD_OK);
735 }
736
737 static int
print_soutype(const char * sou,uintptr_t addr,uint_t flags)738 print_soutype(const char *sou, uintptr_t addr, uint_t flags)
739 {
740 static const char *prefixes[] = { "struct ", "union " };
741 size_t namesz = 7 + strlen(sou) + 1;
742 char *name = mdb_alloc(namesz, UM_SLEEP | UM_GC);
743 mdb_ctf_id_t id;
744 int i;
745
746 for (i = 0; i < 2; i++) {
747 (void) mdb_snprintf(name, namesz, "%s%s", prefixes[i], sou);
748
749 if (mdb_ctf_lookup_by_name(name, &id) == 0) {
750 mdb_arg_t v;
751 int rv;
752
753 v.a_type = MDB_TYPE_STRING;
754 v.a_un.a_str = name;
755
756 rv = mdb_call_dcmd("print", addr, flags, 1, &v);
757 return (rv);
758 }
759 }
760
761 return (DCMD_ERR);
762 }
763
764 static int
print_type(const char * name,uintptr_t addr,uint_t flags)765 print_type(const char *name, uintptr_t addr, uint_t flags)
766 {
767 mdb_ctf_id_t id;
768 char *sname;
769 size_t snamesz;
770 int rv;
771
772 if (!(flags & DCMD_ADDRSPEC)) {
773 addr = mdb_get_dot();
774 flags |= DCMD_ADDRSPEC;
775 }
776
777 if ((rv = print_soutype(name, addr, flags)) != DCMD_ERR)
778 return (rv);
779
780 snamesz = strlen(name) + 3;
781 sname = mdb_zalloc(snamesz, UM_SLEEP | UM_GC);
782 (void) mdb_snprintf(sname, snamesz, "%s_t", name);
783
784 if (mdb_ctf_lookup_by_name(sname, &id) == 0) {
785 mdb_arg_t v;
786 int rv;
787
788 v.a_type = MDB_TYPE_STRING;
789 v.a_un.a_str = sname;
790
791 rv = mdb_call_dcmd("print", addr, flags, 1, &v);
792 return (rv);
793 }
794
795 sname[snamesz - 2] = 's';
796 rv = print_soutype(sname, addr, flags);
797 return (rv);
798 }
799
800 static int
exec_alias(const char * fname,uintptr_t addr,uint_t flags)801 exec_alias(const char *fname, uintptr_t addr, uint_t flags)
802 {
803 const char *alias;
804 int rv;
805
806 if ((alias = mdb_macalias_lookup(fname)) == NULL)
807 return (DCMD_ERR);
808
809 if (flags & DCMD_ADDRSPEC) {
810 size_t sz = sizeof (uintptr_t) * 2 + strlen(alias) + 1;
811 char *addralias = mdb_alloc(sz, UM_SLEEP | UM_GC);
812 (void) mdb_snprintf(addralias, sz, "%p%s", addr, alias);
813 rv = mdb_eval(addralias);
814 } else {
815 rv = mdb_eval(alias);
816 }
817
818 return (rv == -1 ? DCMD_ABORT : DCMD_OK);
819 }
820
821 /*ARGSUSED*/
822 static int
cmd_src_file(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)823 cmd_src_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
824 {
825 const char *fname;
826 mdb_io_t *fio;
827 int rv;
828
829 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
830 return (DCMD_USAGE);
831
832 fname = argv->a_un.a_str;
833
834 if (flags & DCMD_PIPE_OUT) {
835 mdb_warn("macro files cannot be used as input to a pipeline\n");
836 return (DCMD_ABORT);
837 }
838
839 if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
840 O_RDONLY, 0)) != NULL) {
841 mdb_frame_t *fp = mdb.m_frame;
842 int err;
843
844 mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
845 mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
846 err = mdb_run();
847
848 ASSERT(fp == mdb.m_frame);
849 mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
850 yylineno = mdb_iob_lineno(mdb.m_in);
851
852 if (err == MDB_ERR_PAGER && mdb.m_fmark != fp)
853 longjmp(fp->f_pcb, err);
854
855 if (err == MDB_ERR_QUIT || err == MDB_ERR_ABORT ||
856 err == MDB_ERR_SIGINT || err == MDB_ERR_OUTPUT)
857 longjmp(fp->f_pcb, err);
858
859 return (DCMD_OK);
860 }
861
862 if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
863 (rv = print_type(fname, addr, flags)) != DCMD_ERR)
864 return (rv);
865
866 mdb_warn("failed to open %s (see ::help '$<')\n", fname);
867 return (DCMD_ABORT);
868 }
869
870 static int
cmd_exec_file(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)871 cmd_exec_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
872 {
873 const char *fname;
874 mdb_io_t *fio;
875 int rv;
876
877 /*
878 * The syntax [expr[,count]]$< with no trailing macro file name is
879 * magic in that if count is zero, this command won't be called and
880 * the expression is thus a no-op. If count is non-zero, we get
881 * invoked with argc == 0, and this means abort the current macro.
882 * If our debugger stack depth is greater than one, we may be using
883 * $< from within a previous $<<, so in that case we set m_in to
884 * NULL to force this entire frame to be popped.
885 */
886 if (argc == 0) {
887 if (mdb_iob_stack_size(&mdb.m_frame->f_istk) != 0) {
888 mdb_iob_destroy(mdb.m_in);
889 mdb.m_in = mdb_iob_stack_pop(&mdb.m_frame->f_istk);
890 } else if (mdb.m_depth > 1) {
891 mdb_iob_destroy(mdb.m_in);
892 mdb.m_in = NULL;
893 } else
894 mdb_warn("input stack is empty\n");
895 return (DCMD_OK);
896 }
897
898 if ((flags & (DCMD_PIPE | DCMD_PIPE_OUT)) || mdb.m_depth == 1)
899 return (cmd_src_file(addr, flags, argc, argv));
900
901 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
902 return (DCMD_USAGE);
903
904 fname = argv->a_un.a_str;
905
906 if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
907 O_RDONLY, 0)) != NULL) {
908 mdb_iob_destroy(mdb.m_in);
909 mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
910 return (DCMD_OK);
911 }
912
913 if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
914 (rv = print_type(fname, addr, flags)) != DCMD_ERR)
915 return (rv);
916
917 mdb_warn("failed to open %s (see ::help '$<')\n", fname);
918 return (DCMD_ABORT);
919 }
920
921 #ifndef _KMDB
922 /*ARGSUSED*/
923 static int
cmd_cat(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)924 cmd_cat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
925 {
926 int status = DCMD_OK;
927 char buf[BUFSIZ];
928 mdb_iob_t *iob;
929 mdb_io_t *fio;
930
931 if (flags & DCMD_ADDRSPEC)
932 return (DCMD_USAGE);
933
934 for (; argc-- != 0; argv++) {
935 if (argv->a_type != MDB_TYPE_STRING) {
936 mdb_warn("expected string argument\n");
937 status = DCMD_ERR;
938 continue;
939 }
940
941 if ((fio = mdb_fdio_create_path(NULL,
942 argv->a_un.a_str, O_RDONLY, 0)) == NULL) {
943 mdb_warn("failed to open %s", argv->a_un.a_str);
944 status = DCMD_ERR;
945 continue;
946 }
947
948 iob = mdb_iob_create(fio, MDB_IOB_RDONLY);
949
950 while (!(mdb_iob_getflags(iob) & (MDB_IOB_EOF | MDB_IOB_ERR))) {
951 ssize_t len = mdb_iob_read(iob, buf, sizeof (buf));
952 if (len > 0) {
953 if (mdb_iob_write(mdb.m_out, buf, len) < 0) {
954 if (errno != EPIPE)
955 mdb_warn("write failed");
956 status = DCMD_ERR;
957 break;
958 }
959 }
960 }
961
962 if (mdb_iob_err(iob))
963 mdb_warn("error while reading %s", mdb_iob_name(iob));
964
965 mdb_iob_destroy(iob);
966 }
967
968 return (status);
969 }
970 #endif
971
972 /*ARGSUSED*/
973 static int
cmd_grep(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)974 cmd_grep(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
975 {
976 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
977 return (DCMD_USAGE);
978
979 if (mdb_eval(argv->a_un.a_str) == -1)
980 return (DCMD_ABORT);
981
982 if (mdb_get_dot() != 0)
983 mdb_printf("%lr\n", addr);
984
985 return (DCMD_OK);
986 }
987
988 /*ARGSUSED*/
989 static int
cmd_map(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)990 cmd_map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
991 {
992 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
993 return (DCMD_USAGE);
994
995 if (mdb_eval(argv->a_un.a_str) == -1)
996 return (DCMD_ABORT);
997
998 mdb_printf("%llr\n", mdb_get_dot());
999 return (DCMD_OK);
1000 }
1001
1002 /*ARGSUSED*/
1003 static int
cmd_notsup(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1004 cmd_notsup(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1005 {
1006 mdb_warn("command is not supported by current target\n");
1007 return (DCMD_ERR);
1008 }
1009
1010 /*ARGSUSED*/
1011 static int
cmd_quit(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1012 cmd_quit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1013 {
1014 #ifdef _KMDB
1015 uint_t opt_u = FALSE;
1016
1017 if (mdb_getopts(argc, argv,
1018 'u', MDB_OPT_SETBITS, TRUE, &opt_u, NULL) != argc)
1019 return (DCMD_USAGE);
1020
1021 if (opt_u) {
1022 if (mdb.m_flags & MDB_FL_NOUNLOAD) {
1023 warn("%s\n", mdb_strerror(EMDB_KNOUNLOAD));
1024 return (DCMD_ERR);
1025 }
1026
1027 kmdb_kdi_set_unload_request();
1028 }
1029 #endif
1030
1031 longjmp(mdb.m_frame->f_pcb, MDB_ERR_QUIT);
1032 /*NOTREACHED*/
1033 return (DCMD_ERR);
1034 }
1035
1036 #ifdef _KMDB
1037 static void
quit_help(void)1038 quit_help(void)
1039 {
1040 mdb_printf(
1041 "-u unload the debugger (if not loaded at boot)\n");
1042 }
1043 #endif
1044
1045 /*ARGSUSED*/
1046 static int
cmd_vars(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1047 cmd_vars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1048 {
1049 uint_t opt_nz = FALSE, opt_tag = FALSE, opt_prt = FALSE;
1050 mdb_var_t *v;
1051
1052 if (mdb_getopts(argc, argv,
1053 'n', MDB_OPT_SETBITS, TRUE, &opt_nz,
1054 'p', MDB_OPT_SETBITS, TRUE, &opt_prt,
1055 't', MDB_OPT_SETBITS, TRUE, &opt_tag, NULL) != argc)
1056 return (DCMD_USAGE);
1057
1058 mdb_nv_rewind(&mdb.m_nv);
1059
1060 while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
1061 if ((opt_tag == FALSE || (v->v_flags & MDB_NV_TAGGED)) &&
1062 (opt_nz == FALSE || mdb_nv_get_value(v) != 0)) {
1063 if (opt_prt) {
1064 mdb_printf("%#llr>%s\n",
1065 mdb_nv_get_value(v), mdb_nv_get_name(v));
1066 } else {
1067 mdb_printf("%s = %llr\n",
1068 mdb_nv_get_name(v), mdb_nv_get_value(v));
1069 }
1070 }
1071 }
1072
1073 return (DCMD_OK);
1074 }
1075
1076 /*ARGSUSED*/
1077 static int
cmd_nzvars(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1078 cmd_nzvars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1079 {
1080 uintmax_t value;
1081 mdb_var_t *v;
1082
1083 if (argc != 0)
1084 return (DCMD_USAGE);
1085
1086 mdb_nv_rewind(&mdb.m_nv);
1087
1088 while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
1089 if ((value = mdb_nv_get_value(v)) != 0)
1090 mdb_printf("%s = %llr\n", mdb_nv_get_name(v), value);
1091 }
1092
1093 return (DCMD_OK);
1094 }
1095
1096 /*ARGSUSED*/
1097 static int
cmd_radix(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1098 cmd_radix(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1099 {
1100 if (argc != 0)
1101 return (DCMD_USAGE);
1102
1103 if (flags & DCMD_ADDRSPEC) {
1104 if (addr < 2 || addr > 16) {
1105 mdb_warn("expected radix from 2 to 16\n");
1106 return (DCMD_ERR);
1107 }
1108 mdb.m_radix = (int)addr;
1109 }
1110
1111 mdb_iob_printf(mdb.m_out, "radix = %d base ten\n", mdb.m_radix);
1112 return (DCMD_OK);
1113 }
1114
1115 /*ARGSUSED*/
1116 static int
cmd_symdist(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1117 cmd_symdist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1118 {
1119 if (argc != 0)
1120 return (DCMD_USAGE);
1121
1122 if (flags & DCMD_ADDRSPEC)
1123 mdb.m_symdist = addr;
1124
1125 mdb_printf("symbol matching distance = %lr (%s)\n",
1126 mdb.m_symdist, mdb.m_symdist ? "absolute mode" : "smart mode");
1127
1128 return (DCMD_OK);
1129 }
1130
1131 /*ARGSUSED*/
1132 static int
cmd_pgwidth(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1133 cmd_pgwidth(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1134 {
1135 if (argc != 0)
1136 return (DCMD_USAGE);
1137
1138 if (flags & DCMD_ADDRSPEC)
1139 mdb_iob_resize(mdb.m_out, mdb.m_out->iob_rows, addr);
1140
1141 mdb_printf("output page width = %lu\n", mdb.m_out->iob_cols);
1142 return (DCMD_OK);
1143 }
1144
1145 /*ARGSUSED*/
1146 static int
cmd_reopen(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1147 cmd_reopen(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1148 {
1149 if (argc != 0)
1150 return (DCMD_USAGE);
1151
1152 if (mdb_tgt_setflags(mdb.m_target, MDB_TGT_F_RDWR) == -1) {
1153 mdb_warn("failed to re-open target for writing");
1154 return (DCMD_ERR);
1155 }
1156
1157 return (DCMD_OK);
1158 }
1159
1160 /*ARGSUSED*/
1161 static int
print_xdata(void * ignored,const char * name,const char * desc,size_t nbytes)1162 print_xdata(void *ignored, const char *name, const char *desc, size_t nbytes)
1163 {
1164 mdb_printf("%-24s - %s (%lu bytes)\n", name, desc, (ulong_t)nbytes);
1165 return (0);
1166 }
1167
1168 /*ARGSUSED*/
1169 static int
cmd_xdata(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1170 cmd_xdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1171 {
1172 if (argc != 0 || (flags & DCMD_ADDRSPEC))
1173 return (DCMD_USAGE);
1174
1175 (void) mdb_tgt_xdata_iter(mdb.m_target, print_xdata, NULL);
1176 return (DCMD_OK);
1177 }
1178
1179 /*ARGSUSED*/
1180 static int
cmd_unset(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1181 cmd_unset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1182 {
1183 mdb_var_t *v;
1184 size_t i;
1185
1186 for (i = 0; i < argc; i++) {
1187 if (argv[i].a_type != MDB_TYPE_STRING) {
1188 mdb_warn("bad option: arg %lu is not a string\n",
1189 (ulong_t)i + 1);
1190 return (DCMD_USAGE);
1191 }
1192 }
1193
1194 for (i = 0; i < argc; i++, argv++) {
1195 if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
1196 mdb_warn("variable '%s' not defined\n",
1197 argv->a_un.a_str);
1198 else
1199 mdb_nv_remove(&mdb.m_nv, v);
1200 }
1201
1202 return (DCMD_OK);
1203 }
1204
1205 #ifndef _KMDB
1206 /*ARGSUSED*/
1207 static int
cmd_log(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1208 cmd_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1209 {
1210 uint_t opt_e = FALSE, opt_d = FALSE;
1211 const char *filename = NULL;
1212 int i;
1213
1214 i = mdb_getopts(argc, argv,
1215 'd', MDB_OPT_SETBITS, TRUE, &opt_d,
1216 'e', MDB_OPT_SETBITS, TRUE, &opt_e, NULL);
1217
1218 if ((i != argc && i != argc - 1) || (opt_d && opt_e) ||
1219 (i != argc && argv[i].a_type != MDB_TYPE_STRING) ||
1220 (i != argc && opt_d == TRUE) || (flags & DCMD_ADDRSPEC))
1221 return (DCMD_USAGE);
1222
1223 if (mdb.m_depth != 1) {
1224 mdb_warn("log may not be manipulated in this context\n");
1225 return (DCMD_ABORT);
1226 }
1227
1228 if (i != argc)
1229 filename = argv[i].a_un.a_str;
1230
1231 /*
1232 * If no arguments were specified, print the log file name (if any)
1233 * and report whether the log is enabled or disabled.
1234 */
1235 if (argc == 0) {
1236 if (mdb.m_log) {
1237 mdb_printf("%s: logging to \"%s\" is currently %s\n",
1238 mdb.m_pname, IOP_NAME(mdb.m_log),
1239 mdb.m_flags & MDB_FL_LOG ? "enabled" : "disabled");
1240 } else
1241 mdb_printf("%s: no log is active\n", mdb.m_pname);
1242 return (DCMD_OK);
1243 }
1244
1245 /*
1246 * If the -d option was specified, pop the log i/o object off the
1247 * i/o stack of stdin, stdout, and stderr.
1248 */
1249 if (opt_d) {
1250 if (mdb.m_flags & MDB_FL_LOG) {
1251 (void) mdb_iob_pop_io(mdb.m_in);
1252 (void) mdb_iob_pop_io(mdb.m_out);
1253 (void) mdb_iob_pop_io(mdb.m_err);
1254 mdb.m_flags &= ~MDB_FL_LOG;
1255 } else
1256 mdb_warn("logging is already disabled\n");
1257 return (DCMD_OK);
1258 }
1259
1260 /*
1261 * The -e option is the default: (re-)enable logging by pushing
1262 * the log i/o object on to stdin, stdout, and stderr. If we have
1263 * a previous log file, we need to pop it and close it. If we have
1264 * no new log file, push the previous one back on.
1265 */
1266 if (filename != NULL) {
1267 if (mdb.m_log != NULL) {
1268 if (mdb.m_flags & MDB_FL_LOG) {
1269 (void) mdb_iob_pop_io(mdb.m_in);
1270 (void) mdb_iob_pop_io(mdb.m_out);
1271 (void) mdb_iob_pop_io(mdb.m_err);
1272 mdb.m_flags &= ~MDB_FL_LOG;
1273 }
1274 mdb_io_rele(mdb.m_log);
1275 }
1276
1277 mdb.m_log = mdb_fdio_create_path(NULL, filename,
1278 O_CREAT | O_APPEND | O_WRONLY, 0666);
1279
1280 if (mdb.m_log == NULL) {
1281 mdb_warn("failed to open %s", filename);
1282 return (DCMD_ERR);
1283 }
1284 }
1285
1286 if (mdb.m_log != NULL) {
1287 mdb_iob_push_io(mdb.m_in, mdb_logio_create(mdb.m_log));
1288 mdb_iob_push_io(mdb.m_out, mdb_logio_create(mdb.m_log));
1289 mdb_iob_push_io(mdb.m_err, mdb_logio_create(mdb.m_log));
1290
1291 mdb_printf("%s: logging to \"%s\"\n", mdb.m_pname, filename);
1292 mdb.m_log = mdb_io_hold(mdb.m_log);
1293 mdb.m_flags |= MDB_FL_LOG;
1294
1295 return (DCMD_OK);
1296 }
1297
1298 mdb_warn("no log file has been selected\n");
1299 return (DCMD_ERR);
1300 }
1301
1302 static int
cmd_old_log(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1303 cmd_old_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1304 {
1305 if (argc == 0) {
1306 mdb_arg_t arg = { MDB_TYPE_STRING, MDB_INIT_STRING("-d") };
1307 return (cmd_log(addr, flags, 1, &arg));
1308 }
1309
1310 return (cmd_log(addr, flags, argc, argv));
1311 }
1312 #endif
1313
1314 /*ARGSUSED*/
1315 static int
cmd_load(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1316 cmd_load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1317 {
1318 int i, mode = MDB_MOD_LOCAL;
1319
1320 i = mdb_getopts(argc, argv,
1321 #ifdef _KMDB
1322 'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
1323 #endif
1324 'f', MDB_OPT_SETBITS, MDB_MOD_FORCE, &mode,
1325 'g', MDB_OPT_SETBITS, MDB_MOD_GLOBAL, &mode,
1326 's', MDB_OPT_SETBITS, MDB_MOD_SILENT, &mode,
1327 NULL);
1328
1329 argc -= i;
1330 argv += i;
1331
1332 if ((flags & DCMD_ADDRSPEC) || argc != 1 ||
1333 argv->a_type != MDB_TYPE_STRING ||
1334 strchr("+-", argv->a_un.a_str[0]) != NULL)
1335 return (DCMD_USAGE);
1336
1337 if (mdb_module_load(argv->a_un.a_str, mode) < 0)
1338 return (DCMD_ERR);
1339
1340 return (DCMD_OK);
1341 }
1342
1343 static void
load_help(void)1344 load_help(void)
1345 {
1346 mdb_printf(
1347 #ifdef _KMDB
1348 "-d defer load until next continue\n"
1349 #endif
1350 "-s load module silently\n");
1351 }
1352
1353 /*ARGSUSED*/
1354 static int
cmd_unload(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1355 cmd_unload(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1356 {
1357 int mode = 0;
1358 int i;
1359
1360 i = mdb_getopts(argc, argv,
1361 #ifdef _KMDB
1362 'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
1363 #endif
1364 NULL);
1365
1366 argc -= i;
1367 argv += i;
1368
1369 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
1370 return (DCMD_USAGE);
1371
1372 if (mdb_module_unload(argv->a_un.a_str, mode) == -1) {
1373 mdb_warn("failed to unload %s", argv->a_un.a_str);
1374 return (DCMD_ERR);
1375 }
1376
1377 return (DCMD_OK);
1378 }
1379
1380 #ifdef _KMDB
1381 static void
unload_help(void)1382 unload_help(void)
1383 {
1384 mdb_printf(
1385 "-d defer unload until next continue\n");
1386 }
1387 #endif
1388
1389 static int
cmd_dbmode(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1390 cmd_dbmode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1391 {
1392 if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
1393 return (DCMD_USAGE);
1394
1395 if (argc != 0) {
1396 if (argv->a_type != MDB_TYPE_STRING)
1397 return (DCMD_USAGE);
1398 if ((addr = mdb_dstr2mode(argv->a_un.a_str)) != MDB_DBG_HELP)
1399 mdb_dmode(addr);
1400 } else if (flags & DCMD_ADDRSPEC)
1401 mdb_dmode(addr);
1402
1403 mdb_printf("debugging mode = 0x%04x\n", mdb.m_debug);
1404 return (DCMD_OK);
1405 }
1406
1407 /*ARGSUSED*/
1408 static int
cmd_version(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1409 cmd_version(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1410 {
1411 #ifdef DEBUG
1412 mdb_printf("\r%s (DEBUG)\n", mdb_conf_version());
1413 #else
1414 mdb_printf("\r%s\n", mdb_conf_version());
1415 #endif
1416 return (DCMD_OK);
1417 }
1418
1419 /*ARGSUSED*/
1420 static int
cmd_algol(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1421 cmd_algol(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1422 {
1423 if (mdb.m_flags & MDB_FL_ADB)
1424 mdb_printf("No algol 68 here\n");
1425 else
1426 mdb_printf("No adb here\n");
1427 return (DCMD_OK);
1428 }
1429
1430 /*ARGSUSED*/
1431 static int
cmd_obey(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1432 cmd_obey(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1433 {
1434 if (mdb.m_flags & MDB_FL_ADB)
1435 mdb_printf("CHAPTER 1\n");
1436 else
1437 mdb_printf("No Language H here\n");
1438 return (DCMD_OK);
1439 }
1440
1441 /*ARGSUSED*/
1442 static int
print_global(void * data,const GElf_Sym * sym,const char * name,const mdb_syminfo_t * sip,const char * obj)1443 print_global(void *data, const GElf_Sym *sym, const char *name,
1444 const mdb_syminfo_t *sip, const char *obj)
1445 {
1446 uintptr_t value;
1447
1448 if (mdb_tgt_vread((mdb_tgt_t *)data, &value, sizeof (value),
1449 (uintptr_t)sym->st_value) == sizeof (value))
1450 mdb_printf("%s(%llr):\t%lr\n", name, sym->st_value, value);
1451 else
1452 mdb_printf("%s(%llr):\t?\n", name, sym->st_value);
1453
1454 return (0);
1455 }
1456
1457 /*ARGSUSED*/
1458 static int
cmd_globals(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1459 cmd_globals(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1460 {
1461 if (argc != 0)
1462 return (DCMD_USAGE);
1463
1464 (void) mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
1465 MDB_TGT_SYMTAB, MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_OBJECT |
1466 MDB_TGT_TYPE_FUNC, print_global, mdb.m_target);
1467
1468 return (0);
1469 }
1470
1471 /*ARGSUSED*/
1472 static int
cmd_eval(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1473 cmd_eval(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1474 {
1475 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
1476 return (DCMD_USAGE);
1477
1478 if (mdb_eval(argv->a_un.a_str) == -1)
1479 return (DCMD_ABORT);
1480
1481 return (DCMD_OK);
1482 }
1483
1484 /*ARGSUSED*/
1485 static int
print_file(void * data,const GElf_Sym * sym,const char * name,const mdb_syminfo_t * sip,const char * obj)1486 print_file(void *data, const GElf_Sym *sym, const char *name,
1487 const mdb_syminfo_t *sip, const char *obj)
1488 {
1489 int i = *((int *)data);
1490
1491 mdb_printf("%d\t%s\n", i++, name);
1492 *((int *)data) = i;
1493 return (0);
1494 }
1495
1496 /*ARGSUSED*/
1497 static int
cmd_files(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1498 cmd_files(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1499 {
1500 int i = 1;
1501 const char *obj = MDB_TGT_OBJ_EVERY;
1502
1503 if ((flags & DCMD_ADDRSPEC) || argc > 1)
1504 return (DCMD_USAGE);
1505
1506 if (argc == 1) {
1507 if (argv->a_type != MDB_TYPE_STRING)
1508 return (DCMD_USAGE);
1509
1510 obj = argv->a_un.a_str;
1511 }
1512
1513 (void) mdb_tgt_symbol_iter(mdb.m_target, obj, MDB_TGT_SYMTAB,
1514 MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FILE, print_file, &i);
1515
1516 return (DCMD_OK);
1517 }
1518
1519 static const char *
map_name(const mdb_map_t * map,const char * name)1520 map_name(const mdb_map_t *map, const char *name)
1521 {
1522 if (map->map_flags & MDB_TGT_MAP_HEAP)
1523 return ("[ heap ]");
1524 if (name != NULL && name[0] != 0)
1525 return (name);
1526
1527 if (map->map_flags & MDB_TGT_MAP_SHMEM)
1528 return ("[ shmem ]");
1529 if (map->map_flags & MDB_TGT_MAP_STACK)
1530 return ("[ stack ]");
1531 if (map->map_flags & MDB_TGT_MAP_ANON)
1532 return ("[ anon ]");
1533 if (map->map_name[0] == '\0')
1534 return ("[ unknown ]");
1535 return (map->map_name);
1536 }
1537
1538 /*ARGSUSED*/
1539 static int
print_map(void * ignored,const mdb_map_t * map,const char * name)1540 print_map(void *ignored, const mdb_map_t *map, const char *name)
1541 {
1542 name = map_name(map, name);
1543
1544 mdb_printf("%?p %?p %?lx %s\n", map->map_base,
1545 map->map_base + map->map_size, map->map_size, name);
1546 return (0);
1547 }
1548
1549 static int
cmd_mappings(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1550 cmd_mappings(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1551 {
1552 const mdb_map_t *m;
1553
1554 if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
1555 return (DCMD_USAGE);
1556
1557 mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
1558 "BASE", "LIMIT", "SIZE", "NAME");
1559
1560 if (flags & DCMD_ADDRSPEC) {
1561 if ((m = mdb_tgt_addr_to_map(mdb.m_target, addr)) == NULL)
1562 mdb_warn("failed to obtain mapping");
1563 else
1564 (void) print_map(NULL, m, NULL);
1565
1566 } else if (argc != 0) {
1567 if (argv->a_type == MDB_TYPE_STRING)
1568 m = mdb_tgt_name_to_map(mdb.m_target, argv->a_un.a_str);
1569 else
1570 m = mdb_tgt_addr_to_map(mdb.m_target, argv->a_un.a_val);
1571
1572 if (m == NULL)
1573 mdb_warn("failed to obtain mapping");
1574 else
1575 (void) print_map(NULL, m, NULL);
1576
1577 } else if (mdb_tgt_mapping_iter(mdb.m_target, print_map, NULL) == -1)
1578 mdb_warn("failed to iterate over mappings");
1579
1580 return (DCMD_OK);
1581 }
1582
1583 static int
whatis_map_callback(void * wp,const mdb_map_t * map,const char * name)1584 whatis_map_callback(void *wp, const mdb_map_t *map, const char *name)
1585 {
1586 mdb_whatis_t *w = wp;
1587 uintptr_t cur;
1588
1589 name = map_name(map, name);
1590
1591 while (mdb_whatis_match(w, map->map_base, map->map_size, &cur))
1592 mdb_whatis_report_address(w, cur, "in %s [%p,%p)\n",
1593 name, map->map_base, map->map_base + map->map_size);
1594
1595 return (0);
1596 }
1597
1598 /*ARGSUSED*/
1599 int
whatis_run_mappings(mdb_whatis_t * w,void * ignored)1600 whatis_run_mappings(mdb_whatis_t *w, void *ignored)
1601 {
1602 (void) mdb_tgt_mapping_iter(mdb.m_target, whatis_map_callback, w);
1603 return (0);
1604 }
1605
1606 /*ARGSUSED*/
1607 static int
objects_printversion(void * ignored,const mdb_map_t * map,const char * name)1608 objects_printversion(void *ignored, const mdb_map_t *map, const char *name)
1609 {
1610 ctf_file_t *ctfp;
1611 const char *version;
1612
1613 ctfp = mdb_tgt_name_to_ctf(mdb.m_target, name);
1614 if (ctfp == NULL || (version = ctf_label_topmost(ctfp)) == NULL)
1615 version = "Unknown";
1616
1617 mdb_printf("%-28s %s\n", name, version);
1618 return (0);
1619 }
1620
1621 /*ARGSUSED*/
1622 static int
cmd_objects(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1623 cmd_objects(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1624 {
1625 uint_t opt_v = FALSE;
1626 mdb_tgt_map_f *cb;
1627
1628 if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
1629 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
1630 return (DCMD_USAGE);
1631
1632 if (opt_v) {
1633 cb = objects_printversion;
1634 mdb_printf("%<u>%-28s %s%</u>\n", "NAME", "VERSION");
1635 } else {
1636 cb = print_map;
1637 mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
1638 "BASE", "LIMIT", "SIZE", "NAME");
1639 }
1640
1641 if (mdb_tgt_object_iter(mdb.m_target, cb, NULL) == -1) {
1642 mdb_warn("failed to iterate over objects");
1643 return (DCMD_ERR);
1644 }
1645
1646 return (DCMD_OK);
1647 }
1648
1649 /*ARGSUSED*/
1650 static int
showrev_addversion(void * vers_nv,const mdb_map_t * ignored,const char * object)1651 showrev_addversion(void *vers_nv, const mdb_map_t *ignored, const char *object)
1652 {
1653 ctf_file_t *ctfp;
1654 const char *version = NULL;
1655 char *objname;
1656
1657 objname = mdb_alloc(strlen(object) + 1, UM_SLEEP | UM_GC);
1658 (void) strcpy(objname, object);
1659
1660 if ((ctfp = mdb_tgt_name_to_ctf(mdb.m_target, objname)) != NULL)
1661 version = ctf_label_topmost(ctfp);
1662
1663 /*
1664 * Not all objects have CTF and label data, so set version to "Unknown".
1665 */
1666 if (version == NULL)
1667 version = "Unknown";
1668
1669 (void) mdb_nv_insert(vers_nv, version, NULL, (uintptr_t)objname,
1670 MDB_NV_OVERLOAD);
1671
1672 return (0);
1673 }
1674
1675 static int
showrev_ispatch(const char * s)1676 showrev_ispatch(const char *s)
1677 {
1678 if (s == NULL)
1679 return (0);
1680
1681 if (*s == 'T')
1682 s++; /* skip T for T-patch */
1683
1684 for (; *s != '\0'; s++) {
1685 if ((*s < '0' || *s > '9') && *s != '-')
1686 return (0);
1687 }
1688
1689 return (1);
1690 }
1691
1692 /*ARGSUSED*/
1693 static int
showrev_printobject(mdb_var_t * v,void * ignored)1694 showrev_printobject(mdb_var_t *v, void *ignored)
1695 {
1696 mdb_printf("%s ", MDB_NV_COOKIE(v));
1697 return (0);
1698 }
1699
1700 static int
showrev_printversion(mdb_var_t * v,void * showall)1701 showrev_printversion(mdb_var_t *v, void *showall)
1702 {
1703 const char *version = mdb_nv_get_name(v);
1704 int patch;
1705
1706 patch = showrev_ispatch(version);
1707 if (patch || (uintptr_t)showall) {
1708 mdb_printf("%s: %s Objects: ",
1709 (patch ? "Patch" : "Version"), version);
1710 (void) mdb_inc_indent(2);
1711
1712 mdb_nv_defn_iter(v, showrev_printobject, NULL);
1713
1714 (void) mdb_dec_indent(2);
1715 mdb_printf("\n");
1716 }
1717
1718 return (0);
1719 }
1720
1721 /*
1722 * Display version information for each object in the system.
1723 * Print information about patches only, unless showall is TRUE.
1724 */
1725 static int
showrev_objectversions(int showall)1726 showrev_objectversions(int showall)
1727 {
1728 mdb_nv_t vers_nv;
1729
1730 (void) mdb_nv_create(&vers_nv, UM_SLEEP | UM_GC);
1731 if (mdb_tgt_object_iter(mdb.m_target, showrev_addversion,
1732 &vers_nv) == -1) {
1733 mdb_warn("failed to iterate over objects");
1734 return (DCMD_ERR);
1735 }
1736
1737 mdb_nv_sort_iter(&vers_nv, showrev_printversion,
1738 (void *)(uintptr_t)showall, UM_SLEEP | UM_GC);
1739 return (DCMD_OK);
1740 }
1741
1742 /*
1743 * Display information similar to what showrev(8) displays when invoked
1744 * with no arguments.
1745 */
1746 static int
showrev_sysinfo(void)1747 showrev_sysinfo(void)
1748 {
1749 const char *s;
1750 int rc;
1751 struct utsname u;
1752
1753 if ((rc = mdb_tgt_uname(mdb.m_target, &u)) != -1) {
1754 mdb_printf("Hostname: %s\n", u.nodename);
1755 mdb_printf("Release: %s\n", u.release);
1756 mdb_printf("Kernel architecture: %s\n", u.machine);
1757 }
1758
1759 /*
1760 * Match the order of the showrev(8) output and put "Application
1761 * architecture" before "Kernel version"
1762 */
1763 if ((s = mdb_tgt_isa(mdb.m_target)) != NULL)
1764 mdb_printf("Application architecture: %s\n", s);
1765
1766 if (rc != -1)
1767 mdb_printf("Kernel version: %s %s %s %s\n",
1768 u.sysname, u.release, u.machine, u.version);
1769
1770 if ((s = mdb_tgt_platform(mdb.m_target)) != NULL)
1771 mdb_printf("Platform: %s\n", s);
1772
1773 return (DCMD_OK);
1774 }
1775
1776 /*ARGSUSED*/
1777 static int
cmd_showrev(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1778 cmd_showrev(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1779 {
1780 uint_t opt_p = FALSE, opt_v = FALSE;
1781
1782 if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
1783 'p', MDB_OPT_SETBITS, TRUE, &opt_p,
1784 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
1785 return (DCMD_USAGE);
1786
1787 if (opt_p || opt_v)
1788 return (showrev_objectversions(opt_v));
1789 else
1790 return (showrev_sysinfo());
1791 }
1792
1793 #ifdef __sparc
1794 static void
findsym_output(uintptr_t * symlist,uintptr_t value,uintptr_t location)1795 findsym_output(uintptr_t *symlist, uintptr_t value, uintptr_t location)
1796 {
1797 uintptr_t *symbolp;
1798
1799 for (symbolp = symlist; *symbolp; symbolp++)
1800 if (value == *symbolp)
1801 mdb_printf("found %a at %a\n", value, location);
1802 }
1803
1804 /*ARGSUSED*/
1805 static int
findsym_cb(void * data,const GElf_Sym * sym,const char * name,const mdb_syminfo_t * sip,const char * obj)1806 findsym_cb(void *data, const GElf_Sym *sym, const char *name,
1807 const mdb_syminfo_t *sip, const char *obj)
1808 {
1809 uint32_t *text;
1810 int len;
1811 int i;
1812 int j;
1813 uint8_t rd;
1814 uintptr_t value;
1815 int32_t imm13;
1816 uint8_t op;
1817 uint8_t op3;
1818 uintptr_t *symlist = data;
1819 size_t size = sym->st_size;
1820
1821 /*
1822 * if the size of the symbol is 0, then this symbol must be for an
1823 * alternate entry point or just some global label. We will,
1824 * therefore, get back to the text that follows this symbol in
1825 * some other symbol
1826 */
1827 if (size == 0)
1828 return (0);
1829
1830 if (sym->st_shndx == SHN_UNDEF)
1831 return (0);
1832
1833 text = alloca(size);
1834
1835 if (mdb_vread(text, size, sym->st_value) == -1) {
1836 mdb_warn("failed to read text for %s", name);
1837 return (0);
1838 }
1839
1840 len = size / 4;
1841 for (i = 0; i < len; i++) {
1842 if (!IS_SETHI(text[i]))
1843 continue;
1844
1845 rd = RD(text[i]);
1846 value = IMM22(text[i]) << 10;
1847
1848 /*
1849 * see if we already have a match with just the sethi
1850 */
1851 findsym_output(symlist, value, sym->st_value + i * 4);
1852
1853 /*
1854 * search from the sethi on until we hit a relevant instr
1855 */
1856 for (j = i + 1; j < len; j++) {
1857 if ((op = OP(text[j])) & OP_ARITH_MEM_MASK) {
1858 op3 = OP3(text[j]);
1859
1860 if (RS1(text[j]) != rd)
1861 goto instr_end;
1862
1863 /*
1864 * This is a simple tool; we only deal
1865 * with operations which take immediates
1866 */
1867 if (I(text[j]) == 0)
1868 goto instr_end;
1869
1870 /*
1871 * sign extend the immediate value
1872 */
1873 imm13 = IMM13(text[j]);
1874 imm13 <<= 19;
1875 imm13 >>= 19;
1876
1877 if (op == OP_ARITH) {
1878 /* arithmetic operations */
1879 if (op3 & OP3_COMPLEX_MASK)
1880 goto instr_end;
1881
1882 switch (op3 & ~OP3_CC_MASK) {
1883 case OP3_OR:
1884 value |= imm13;
1885 break;
1886 case OP3_ADD:
1887 value += imm13;
1888 break;
1889 case OP3_XOR:
1890 value ^= imm13;
1891 break;
1892 default:
1893 goto instr_end;
1894 }
1895 } else {
1896 /* loads and stores */
1897 /* op3 == OP_MEM */
1898
1899 value += imm13;
1900 }
1901
1902 findsym_output(symlist, value,
1903 sym->st_value + j * 4);
1904 instr_end:
1905 /*
1906 * if we're clobbering rd, break
1907 */
1908 if (RD(text[j]) == rd)
1909 break;
1910 } else if (IS_SETHI(text[j])) {
1911 if (RD(text[j]) == rd)
1912 break;
1913 } else if (OP(text[j]) == 1) {
1914 /*
1915 * see if a call clobbers an %o or %g
1916 */
1917 if (rd <= R_O7)
1918 break;
1919 }
1920 }
1921 }
1922
1923 return (0);
1924 }
1925
1926 static int
cmd_findsym(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)1927 cmd_findsym(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1928 {
1929 uintptr_t *symlist;
1930 uint_t optg = FALSE;
1931 uint_t type;
1932 int len, i;
1933
1934 i = mdb_getopts(argc, argv, 'g', MDB_OPT_SETBITS, TRUE, &optg, NULL);
1935
1936 argc -= i;
1937 argv += i;
1938
1939 len = argc + ((flags & DCMD_ADDRSPEC) ? 1 : 0) + 1;
1940
1941 if (len <= 1)
1942 return (DCMD_USAGE);
1943
1944 /*
1945 * Set up a NULL-terminated symbol list, and then iterate over the
1946 * symbol table, scanning each function for references to these symbols.
1947 */
1948 symlist = mdb_alloc(len * sizeof (uintptr_t), UM_SLEEP | UM_GC);
1949 len = 0;
1950
1951 for (i = 0; i < argc; i++, argv++) {
1952 const char *str = argv->a_un.a_str;
1953 uintptr_t value;
1954 GElf_Sym sym;
1955
1956 if (argv->a_type == MDB_TYPE_STRING) {
1957 if (strchr("+-", str[0]) != NULL)
1958 return (DCMD_USAGE);
1959 else if (str[0] >= '0' && str[0] <= '9')
1960 value = mdb_strtoull(str);
1961 else if (mdb_lookup_by_name(str, &sym) != 0) {
1962 mdb_warn("symbol '%s' not found", str);
1963 return (DCMD_USAGE);
1964 } else
1965 value = sym.st_value;
1966 } else
1967 value = argv[i].a_un.a_val;
1968
1969 if (value != (uintptr_t)NULL)
1970 symlist[len++] = value;
1971 }
1972
1973 if (flags & DCMD_ADDRSPEC)
1974 symlist[len++] = addr;
1975
1976 symlist[len] = (uintptr_t)NULL;
1977
1978 if (optg)
1979 type = MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_FUNC;
1980 else
1981 type = MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FUNC;
1982
1983 if (mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
1984 MDB_TGT_SYMTAB, type, findsym_cb, symlist) == -1) {
1985 mdb_warn("failed to iterate over symbol table");
1986 return (DCMD_ERR);
1987 }
1988
1989 return (DCMD_OK);
1990 }
1991 #endif /* __sparc */
1992
1993 static int
dis_str2addr(const char * s,uintptr_t * addr)1994 dis_str2addr(const char *s, uintptr_t *addr)
1995 {
1996 GElf_Sym sym;
1997
1998 if (s[0] >= '0' && s[0] <= '9') {
1999 *addr = (uintptr_t)mdb_strtoull(s);
2000 return (0);
2001 }
2002
2003 if (mdb_tgt_lookup_by_name(mdb.m_target,
2004 MDB_TGT_OBJ_EVERY, s, &sym, NULL) == -1) {
2005 mdb_warn("symbol '%s' not found\n", s);
2006 return (-1);
2007 }
2008
2009 *addr = (uintptr_t)sym.st_value;
2010 return (0);
2011 }
2012
2013 static int
cmd_dis(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2014 cmd_dis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2015 {
2016 mdb_tgt_t *tgt = mdb.m_target;
2017 mdb_disasm_t *dis = mdb.m_disasm;
2018
2019 uintptr_t oaddr, naddr;
2020 mdb_tgt_as_t as;
2021 mdb_tgt_status_t st;
2022 char buf[BUFSIZ];
2023 GElf_Sym sym;
2024 int i;
2025
2026 uint_t opt_f = FALSE; /* File-mode off by default */
2027 uint_t opt_p = FALSE; /* Physical mode off by default */
2028 uint_t opt_w = FALSE; /* Window mode off by default */
2029 uint_t opt_a = FALSE; /* Raw-address mode off by default */
2030 uint_t opt_b = FALSE; /* Address & symbols off by default */
2031 uintptr_t n = -1UL; /* Length of window in instructions */
2032 uintptr_t eaddr = 0; /* Ending address; 0 if limited by n */
2033
2034 i = mdb_getopts(argc, argv,
2035 'f', MDB_OPT_SETBITS, TRUE, &opt_f,
2036 'p', MDB_OPT_SETBITS, TRUE, &opt_p,
2037 'w', MDB_OPT_SETBITS, TRUE, &opt_w,
2038 'a', MDB_OPT_SETBITS, TRUE, &opt_a,
2039 'b', MDB_OPT_SETBITS, TRUE, &opt_b,
2040 'n', MDB_OPT_UINTPTR, &n, NULL);
2041
2042 /*
2043 * Disgusting argument post-processing ... basically the idea is to get
2044 * the target address into addr, which we do by using the specified
2045 * expression value, looking up a string as a symbol name, or by
2046 * using the address specified as dot.
2047 */
2048 if (i != argc) {
2049 if (argc != 0 && (argc - i) == 1) {
2050 if (argv[i].a_type == MDB_TYPE_STRING) {
2051 if (argv[i].a_un.a_str[0] == '-')
2052 return (DCMD_USAGE);
2053
2054 if (dis_str2addr(argv[i].a_un.a_str, &addr))
2055 return (DCMD_ERR);
2056 } else
2057 addr = argv[i].a_un.a_val;
2058 } else
2059 return (DCMD_USAGE);
2060 }
2061
2062 /*
2063 * If we're not in window mode yet, and some type of arguments were
2064 * specified, see if the address corresponds nicely to a function.
2065 * If not, turn on window mode; otherwise disassemble the function.
2066 */
2067 if (opt_w == FALSE && (argc != i || (flags & DCMD_ADDRSPEC))) {
2068 if (mdb_tgt_lookup_by_addr(tgt, addr,
2069 MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0 &&
2070 GELF_ST_TYPE(sym.st_info) == STT_FUNC) {
2071 /*
2072 * If the symbol has a size then set our end address to
2073 * be the end of the function symbol we just located.
2074 */
2075 if (sym.st_size != 0)
2076 eaddr = addr + (uintptr_t)sym.st_size;
2077 } else
2078 opt_w = TRUE;
2079 }
2080
2081 /*
2082 * Window-mode doesn't make sense in a loop.
2083 */
2084 if (flags & DCMD_LOOP)
2085 opt_w = FALSE;
2086
2087 /*
2088 * If -n was explicit, limit output to n instructions;
2089 * otherwise set n to some reasonable default
2090 */
2091 if (n != -1UL)
2092 eaddr = 0;
2093 else
2094 n = 10;
2095
2096 /*
2097 * If the state is IDLE (i.e. no address space), turn on -f.
2098 */
2099 if (mdb_tgt_status(tgt, &st) == 0 && st.st_state == MDB_TGT_IDLE) {
2100 if (opt_p) {
2101 mdb_warn("cannot use -p (physical address mode) when "
2102 "operating on a file\n");
2103 return (DCMD_ERR);
2104 }
2105 opt_f = TRUE;
2106 }
2107
2108 if (opt_f && opt_p) {
2109 mdb_warn("-f (file mode) and -p (physical address mode) "
2110 "cannot be used together\n");
2111 return (DCMD_ERR);
2112 }
2113
2114
2115 if (opt_f)
2116 as = MDB_TGT_AS_FILE;
2117 else if (opt_p)
2118 as = MDB_TGT_AS_PHYS;
2119 else
2120 as = MDB_TGT_AS_VIRT_I;
2121
2122 if (opt_w == FALSE) {
2123 n++;
2124 while ((eaddr == 0 && n-- != 0) || (addr < eaddr)) {
2125 naddr = mdb_dis_ins2str(dis, tgt, as,
2126 buf, sizeof (buf), addr);
2127 if (naddr == addr)
2128 return (DCMD_ERR);
2129 if (opt_a)
2130 mdb_printf("%-#32p%8T%s\n", addr, buf);
2131 else if (opt_b)
2132 mdb_printf("%-#?p %-#32a%8T%s\n",
2133 addr, addr, buf);
2134 else
2135 mdb_printf("%-#32a%8T%s\n", addr, buf);
2136 addr = naddr;
2137 }
2138
2139 } else {
2140 #ifdef __sparc
2141 if (addr & 0x3) {
2142 mdb_warn("address is not properly aligned\n");
2143 return (DCMD_ERR);
2144 }
2145 #endif
2146
2147 for (oaddr = mdb_dis_previns(dis, tgt, as, addr, n);
2148 oaddr < addr; oaddr = naddr) {
2149 naddr = mdb_dis_ins2str(dis, tgt, as,
2150 buf, sizeof (buf), oaddr);
2151 if (naddr == oaddr)
2152 return (DCMD_ERR);
2153 if (opt_a)
2154 mdb_printf("%-#32p%8T%s\n", oaddr, buf);
2155 else if (opt_b)
2156 mdb_printf("%-#?p %-#32a%8T%s\n",
2157 oaddr, oaddr, buf);
2158 else
2159 mdb_printf("%-#32a%8T%s\n", oaddr, buf);
2160 }
2161
2162 if ((naddr = mdb_dis_ins2str(dis, tgt, as,
2163 buf, sizeof (buf), addr)) == addr)
2164 return (DCMD_ERR);
2165
2166 mdb_printf("%<b>");
2167 mdb_flush();
2168 if (opt_a)
2169 mdb_printf("%-#32p%8T%s%", addr, buf);
2170 else if (opt_b)
2171 mdb_printf("%-#?p %-#32a%8T%s", addr, addr, buf);
2172 else
2173 mdb_printf("%-#32a%8T%s%", addr, buf);
2174 mdb_printf("%</b>\n");
2175
2176 for (addr = naddr; n-- != 0; addr = naddr) {
2177 naddr = mdb_dis_ins2str(dis, tgt, as,
2178 buf, sizeof (buf), addr);
2179 if (naddr == addr)
2180 return (DCMD_ERR);
2181 if (opt_a)
2182 mdb_printf("%-#32p%8T%s\n", addr, buf);
2183 else if (opt_b)
2184 mdb_printf("%-#?p %-#32a%8T%s\n",
2185 addr, addr, buf);
2186 else
2187 mdb_printf("%-#32a%8T%s\n", addr, buf);
2188 }
2189 }
2190
2191 mdb_set_dot(addr);
2192 return (DCMD_OK);
2193 }
2194
2195 static void
dis_help(void)2196 dis_help(void)
2197 {
2198 static const char dis_desc[] =
2199 "Disassembles instructions starting at the final argument or the current\n"
2200 "value of dot. If the address is the start of a function, the entire\n"
2201 "function is disassembled, or else a window of instructions before and after\n"
2202 "the disassembled address are displayed.\n"
2203 "\n";
2204
2205 static const char dis_opts[] =
2206 " -a Print instruction addresses as numeric values instead of \n"
2207 " symbolic values.\n"
2208 " -b Print instruction addresses as both numeric and symbolic "
2209 "values.\n"
2210 " -f Read instructions from the target's object file instead of the \n"
2211 " target's virtual address space.\n"
2212 " -p Read instructions from the target's physical address space\n"
2213 " rather than its virtual address space.\n"
2214 " -n instr Display 'instr' instructions before and after the given "
2215 "address.\n"
2216 " -w Force window behavior, even at the start of a function.\n"
2217 "\n";
2218
2219 static const char dis_examples[] =
2220 " ::dis\n"
2221 " clock::dis\n"
2222 " ::dis gethrtime\n"
2223 " set_freemem+0x16::dis -n 4\n"
2224 "\n";
2225
2226 mdb_printf("%s", dis_desc);
2227 (void) mdb_dec_indent(2);
2228 mdb_printf("%<b>OPTIONS%</b>\n");
2229 (void) mdb_inc_indent(2);
2230 mdb_printf("%s", dis_opts);
2231 (void) mdb_dec_indent(2);
2232 mdb_printf("%<b>EXAMPLES%</b>\n");
2233 (void) mdb_inc_indent(2);
2234 (void) mdb_printf("%s", dis_examples);
2235 }
2236
2237 /*ARGSUSED*/
2238 static int
walk_step(uintptr_t addr,const void * data,void * private)2239 walk_step(uintptr_t addr, const void *data, void *private)
2240 {
2241 mdb_printf("%#lr\n", addr);
2242 return (WALK_NEXT);
2243 }
2244
2245 static int
cmd_walk(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2246 cmd_walk(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2247 {
2248 int status;
2249
2250 if (argc < 1 || argc > 2 || argv[0].a_type != MDB_TYPE_STRING ||
2251 argv[argc - 1].a_type != MDB_TYPE_STRING)
2252 return (DCMD_USAGE);
2253
2254 if (argc > 1) {
2255 const char *name = argv[1].a_un.a_str;
2256 mdb_var_t *v = mdb_nv_lookup(&mdb.m_nv, name);
2257 const char *p;
2258
2259 if (v != NULL && (v->v_flags & MDB_NV_RDONLY) != 0) {
2260 mdb_warn("variable %s is read-only\n", name);
2261 return (DCMD_ABORT);
2262 }
2263
2264 if (v == NULL && (p = strbadid(name)) != NULL) {
2265 mdb_warn("'%c' may not be used in a variable "
2266 "name\n", *p);
2267 return (DCMD_ABORT);
2268 }
2269
2270 if (v == NULL && (v = mdb_nv_insert(&mdb.m_nv,
2271 name, NULL, 0, 0)) == NULL)
2272 return (DCMD_ERR);
2273
2274 /*
2275 * If there already exists a vcb for this variable, we may be
2276 * calling ::walk in a loop. We only create a vcb for this
2277 * variable on the first invocation.
2278 */
2279 if (mdb_vcb_find(v, mdb.m_frame) == NULL)
2280 mdb_vcb_insert(mdb_vcb_create(v), mdb.m_frame);
2281 }
2282
2283 if (flags & DCMD_ADDRSPEC)
2284 status = mdb_pwalk(argv->a_un.a_str, walk_step, NULL, addr);
2285 else
2286 status = mdb_walk(argv->a_un.a_str, walk_step, NULL);
2287
2288 if (status == -1) {
2289 mdb_warn("failed to perform walk");
2290 return (DCMD_ERR);
2291 }
2292
2293 return (DCMD_OK);
2294 }
2295
2296 static int
cmd_walk_tab(mdb_tab_cookie_t * mcp,uint_t flags,int argc,const mdb_arg_t * argv)2297 cmd_walk_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
2298 const mdb_arg_t *argv)
2299 {
2300 if (argc > 1)
2301 return (1);
2302
2303 if (argc == 1) {
2304 ASSERT(argv[0].a_type == MDB_TYPE_STRING);
2305 return (mdb_tab_complete_walker(mcp, argv[0].a_un.a_str));
2306 }
2307
2308 if (argc == 0 && flags & DCMD_TAB_SPACE)
2309 return (mdb_tab_complete_walker(mcp, NULL));
2310
2311 return (1);
2312 }
2313
2314 static ssize_t
mdb_partial_xread(void * buf,size_t nbytes,uintptr_t addr,void * arg)2315 mdb_partial_xread(void *buf, size_t nbytes, uintptr_t addr, void *arg)
2316 {
2317 ssize_t (*fp)(mdb_tgt_t *, const void *, size_t, uintptr_t) =
2318 (ssize_t (*)(mdb_tgt_t *, const void *, size_t, uintptr_t))arg;
2319
2320 return (fp(mdb.m_target, buf, nbytes, addr));
2321 }
2322
2323 /* ARGSUSED3 */
2324 static ssize_t
mdb_partial_pread(void * buf,size_t nbytes,physaddr_t addr,void * arg)2325 mdb_partial_pread(void *buf, size_t nbytes, physaddr_t addr, void *arg)
2326 {
2327 return (mdb_tgt_pread(mdb.m_target, buf, nbytes, addr));
2328 }
2329
2330
2331 static int
cmd_dump(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2332 cmd_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2333 {
2334 uint_t dflags =
2335 MDB_DUMP_ALIGN | MDB_DUMP_NEWDOT | MDB_DUMP_ASCII | MDB_DUMP_HEADER;
2336 uint_t phys = FALSE;
2337 uint_t file = FALSE;
2338 uintptr_t group = 4;
2339 uintptr_t length = 0;
2340 uintptr_t width = 1;
2341 mdb_tgt_status_t st;
2342 int error;
2343
2344 if (mdb_getopts(argc, argv,
2345 'e', MDB_OPT_SETBITS, MDB_DUMP_ENDIAN, &dflags,
2346 'f', MDB_OPT_SETBITS, TRUE, &file,
2347 'g', MDB_OPT_UINTPTR, &group,
2348 'l', MDB_OPT_UINTPTR, &length,
2349 'p', MDB_OPT_SETBITS, TRUE, &phys,
2350 'q', MDB_OPT_CLRBITS, MDB_DUMP_ASCII, &dflags,
2351 'r', MDB_OPT_SETBITS, MDB_DUMP_RELATIVE, &dflags,
2352 's', MDB_OPT_SETBITS, MDB_DUMP_SQUISH, &dflags,
2353 't', MDB_OPT_SETBITS, MDB_DUMP_TRIM, &dflags,
2354 'u', MDB_OPT_CLRBITS, MDB_DUMP_ALIGN, &dflags,
2355 'v', MDB_OPT_SETBITS, MDB_DUMP_PEDANT, &dflags,
2356 'w', MDB_OPT_UINTPTR, &width, NULL) != argc)
2357 return (DCMD_USAGE);
2358
2359 if ((phys && file) ||
2360 (width == 0) || (width > 0x10) ||
2361 (group == 0) || (group > 0x100) ||
2362 (mdb.m_dcount > 1 && length > 0))
2363 return (DCMD_USAGE);
2364 if (length == 0)
2365 length = mdb.m_dcount;
2366
2367 /*
2368 * If neither -f nor -p were specified and the state is IDLE (i.e. no
2369 * address space), turn on -p. This is so we can read large files.
2370 */
2371 if (phys == FALSE && file == FALSE && mdb_tgt_status(mdb.m_target,
2372 &st) == 0 && st.st_state == MDB_TGT_IDLE)
2373 phys = TRUE;
2374
2375 dflags |= MDB_DUMP_GROUP(group) | MDB_DUMP_WIDTH(width);
2376 if (phys)
2377 error = mdb_dump64(mdb_get_dot(), length, dflags,
2378 mdb_partial_pread, NULL);
2379 else if (file)
2380 error = mdb_dumpptr(addr, length, dflags,
2381 mdb_partial_xread, (void *)mdb_tgt_fread);
2382 else
2383 error = mdb_dumpptr(addr, length, dflags,
2384 mdb_partial_xread, (void *)mdb_tgt_vread);
2385
2386 return (((flags & DCMD_LOOP) || (error == -1)) ? DCMD_ABORT : DCMD_OK);
2387 }
2388
2389 /*ARGSUSED*/
2390 static int
cmd_echo(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2391 cmd_echo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2392 {
2393 if (flags & DCMD_ADDRSPEC)
2394 return (DCMD_USAGE);
2395
2396 for (; argc-- != 0; argv++) {
2397 if (argv->a_type == MDB_TYPE_STRING)
2398 mdb_printf("%s ", argv->a_un.a_str);
2399 else
2400 mdb_printf("%llr ", argv->a_un.a_val);
2401 }
2402
2403 mdb_printf("\n");
2404 return (DCMD_OK);
2405 }
2406
2407 /*ARGSUSED*/
2408 static int
cmd_head(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2409 cmd_head(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2410 {
2411 uint64_t cnt = 10;
2412 const char *c;
2413 mdb_pipe_t p;
2414
2415 if (!(flags & DCMD_PIPE))
2416 return (DCMD_USAGE);
2417
2418 if (argc == 1 || argc == 2) {
2419 const char *num;
2420
2421 if (argc == 1) {
2422 if (argv[0].a_type != MDB_TYPE_STRING ||
2423 *argv[0].a_un.a_str != '-')
2424 return (DCMD_USAGE);
2425
2426 num = argv[0].a_un.a_str + 1;
2427
2428 } else {
2429 if (argv[0].a_type != MDB_TYPE_STRING ||
2430 strcmp(argv[0].a_un.a_str, "-n") != 0)
2431 return (DCMD_USAGE);
2432
2433 num = argv[1].a_un.a_str;
2434 }
2435
2436 for (cnt = 0, c = num; *c != '\0' && isdigit(*c); c++)
2437 cnt = cnt * 10 + (*c - '0');
2438
2439 if (*c != '\0')
2440 return (DCMD_USAGE);
2441
2442 } else if (argc != 0) {
2443 return (DCMD_USAGE);
2444 }
2445
2446 mdb_get_pipe(&p);
2447
2448 if (p.pipe_data == NULL)
2449 return (DCMD_OK);
2450 p.pipe_len = MIN(p.pipe_len, cnt);
2451
2452 if (flags & DCMD_PIPE_OUT) {
2453 mdb_set_pipe(&p);
2454 } else {
2455 while (p.pipe_len-- > 0)
2456 mdb_printf("%lx\n", *p.pipe_data++);
2457 }
2458
2459 return (DCMD_OK);
2460 }
2461
2462 static void
head_help(void)2463 head_help(void)
2464 {
2465 mdb_printf(
2466 "-n num\n or\n"
2467 "-num pass only the first `num' elements in the pipe.\n"
2468 "\n%<b>Note:%</b> `num' is a decimal number.\n");
2469 }
2470
2471 static int
cmd_typeset(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2472 cmd_typeset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2473 {
2474 int add_tag = 0, del_tag = 0;
2475 const char *p;
2476 mdb_var_t *v;
2477
2478 if (argc == 0)
2479 return (cmd_vars(addr, flags, argc, argv));
2480
2481 if (argv->a_type == MDB_TYPE_STRING && (argv->a_un.a_str[0] == '-' ||
2482 argv->a_un.a_str[0] == '+')) {
2483 if (argv->a_un.a_str[1] != 't')
2484 return (DCMD_USAGE);
2485 if (argv->a_un.a_str[0] == '-')
2486 add_tag++;
2487 else
2488 del_tag++;
2489 argc--;
2490 argv++;
2491 }
2492
2493 if (!(flags & DCMD_ADDRSPEC))
2494 addr = 0; /* set variables to zero unless explicit addr given */
2495
2496 for (; argc-- != 0; argv++) {
2497 if (argv->a_type != MDB_TYPE_STRING)
2498 continue;
2499
2500 if (argv->a_un.a_str[0] == '-' || argv->a_un.a_str[0] == '+') {
2501 mdb_warn("ignored bad option -- %s\n",
2502 argv->a_un.a_str);
2503 continue;
2504 }
2505
2506 if ((p = strbadid(argv->a_un.a_str)) != NULL) {
2507 mdb_warn("'%c' may not be used in a variable "
2508 "name\n", *p);
2509 return (DCMD_ERR);
2510 }
2511
2512 if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL) {
2513 v = mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str,
2514 NULL, addr, 0);
2515 } else if (flags & DCMD_ADDRSPEC)
2516 mdb_nv_set_value(v, addr);
2517
2518 if (v != NULL) {
2519 if (add_tag)
2520 v->v_flags |= MDB_NV_TAGGED;
2521 if (del_tag)
2522 v->v_flags &= ~MDB_NV_TAGGED;
2523 }
2524 }
2525
2526 return (DCMD_OK);
2527 }
2528
2529 #ifndef _KMDB
2530 /*ARGSUSED*/
2531 static int
cmd_context(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2532 cmd_context(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2533 {
2534 if (argc != 0 || !(flags & DCMD_ADDRSPEC))
2535 return (DCMD_USAGE);
2536
2537 if (mdb_tgt_setcontext(mdb.m_target, (void *)addr) == 0)
2538 return (DCMD_OK);
2539
2540 return (DCMD_ERR);
2541 }
2542 #endif
2543
2544 /*ARGSUSED*/
2545 static int
cmd_prompt(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2546 cmd_prompt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2547 {
2548 const char *p = "";
2549
2550 if (argc != 0) {
2551 if (argc > 1 || argv->a_type != MDB_TYPE_STRING)
2552 return (DCMD_USAGE);
2553 p = argv->a_un.a_str;
2554 }
2555
2556 (void) mdb_set_prompt(p);
2557 return (DCMD_OK);
2558 }
2559
2560 /*ARGSUSED*/
2561 static int
cmd_term(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2562 cmd_term(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2563 {
2564 mdb_printf("%s\n", mdb.m_termtype);
2565
2566 return (DCMD_OK);
2567 }
2568
2569 /*ARGSUSED*/
2570 static int
cmd_vtop(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2571 cmd_vtop(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2572 {
2573 physaddr_t pa;
2574 mdb_tgt_as_t as = MDB_TGT_AS_VIRT;
2575
2576 if (mdb_getopts(argc, argv, 'a', MDB_OPT_UINTPTR, (uintptr_t *)&as,
2577 NULL) != argc)
2578 return (DCMD_USAGE);
2579
2580 if (mdb_tgt_vtop(mdb.m_target, as, addr, &pa) == -1) {
2581 mdb_warn("failed to get physical mapping");
2582 return (DCMD_ERR);
2583 }
2584
2585 if (flags & DCMD_PIPE_OUT)
2586 mdb_printf("%llr\n", pa);
2587 else
2588 mdb_printf("virtual %lr mapped to physical %llr\n", addr, pa);
2589 return (DCMD_OK);
2590 }
2591
2592 #define EVENTS_OPT_A 0x1 /* ::events -a (show all events) */
2593 #define EVENTS_OPT_V 0x2 /* ::events -v (verbose display) */
2594
2595 static const char *
event_action(const mdb_tgt_spec_desc_t * sp)2596 event_action(const mdb_tgt_spec_desc_t *sp)
2597 {
2598 if (!(sp->spec_flags & MDB_TGT_SPEC_HIDDEN) && sp->spec_data != NULL)
2599 return (sp->spec_data);
2600
2601 return ("-");
2602 }
2603
2604 static void
print_evsep(void)2605 print_evsep(void)
2606 {
2607 static const char dash20[] = "--------------------";
2608 mdb_printf("----- - -- -- -- %s%s --%s\n", dash20, dash20, dash20);
2609 }
2610
2611 /*ARGSUSED*/
2612 static int
print_event(mdb_tgt_t * t,void * private,int vid,void * data)2613 print_event(mdb_tgt_t *t, void *private, int vid, void *data)
2614 {
2615 uint_t opts = (uint_t)(uintptr_t)private;
2616 mdb_tgt_spec_desc_t sp;
2617 char s1[41], s2[22];
2618 const char *s2str;
2619 int visible;
2620
2621 (void) mdb_tgt_vespec_info(t, vid, &sp, s1, sizeof (s1));
2622 visible = !(sp.spec_flags & (MDB_TGT_SPEC_HIDDEN|MDB_TGT_SPEC_DELETED));
2623
2624 if ((opts & EVENTS_OPT_A) || visible) {
2625 int encoding = (!(sp.spec_flags & MDB_TGT_SPEC_DISABLED)) |
2626 (!(sp.spec_flags & MDB_TGT_SPEC_MATCHED) << 1);
2627
2628 char ldelim = "<<(["[encoding];
2629 char rdelim = ">>)]"[encoding];
2630
2631 char state = "0-+*!"[sp.spec_state];
2632
2633 char tflag = "T "[!(sp.spec_flags & MDB_TGT_SPEC_STICKY)];
2634 char aflag = "d "[!(sp.spec_flags & MDB_TGT_SPEC_AUTODIS)];
2635
2636 if (sp.spec_flags & MDB_TGT_SPEC_TEMPORARY)
2637 tflag = 't'; /* TEMP takes precedence over STICKY */
2638 if (sp.spec_flags & MDB_TGT_SPEC_AUTODEL)
2639 aflag = 'D'; /* AUTODEL takes precedence over AUTODIS */
2640 if (sp.spec_flags & MDB_TGT_SPEC_AUTOSTOP)
2641 aflag = 's'; /* AUTOSTOP takes precedence over both */
2642
2643 if (opts & EVENTS_OPT_V) {
2644 if (sp.spec_state == MDB_TGT_SPEC_IDLE ||
2645 sp.spec_state == MDB_TGT_SPEC_ERROR)
2646 s2str = mdb_strerror(sp.spec_errno);
2647 else
2648 s2str = "-";
2649 } else
2650 s2str = event_action(&sp);
2651
2652 if (mdb_snprintf(s2, sizeof (s2), "%s", s2str) >= sizeof (s2))
2653 (void) strabbr(s2, sizeof (s2));
2654
2655 if (vid > -10 && vid < 10)
2656 mdb_printf("%c%2d %c", ldelim, vid, rdelim);
2657 else
2658 mdb_printf("%c%3d%c", ldelim, vid, rdelim);
2659
2660 mdb_printf(" %c %c%c %2u %2u %-40s %-21s\n",
2661 state, tflag, aflag, sp.spec_hits, sp.spec_limit, s1, s2);
2662
2663 if (opts & EVENTS_OPT_V) {
2664 mdb_printf("%-17s%s\n", "", event_action(&sp));
2665 print_evsep();
2666 }
2667 }
2668
2669 return (0);
2670 }
2671
2672 /*ARGSUSED*/
2673 static int
cmd_events(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2674 cmd_events(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2675 {
2676 uint_t opts = 0;
2677
2678 if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
2679 'a', MDB_OPT_SETBITS, EVENTS_OPT_A, &opts,
2680 'v', MDB_OPT_SETBITS, EVENTS_OPT_V, &opts, NULL) != argc)
2681 return (DCMD_USAGE);
2682
2683
2684 if (opts & EVENTS_OPT_V) {
2685 mdb_printf(" ID S TA HT LM %-40s %-21s\n%-17s%s\n",
2686 "Description", "Status", "", "Action");
2687 } else {
2688 mdb_printf(" ID S TA HT LM %-40s %-21s\n",
2689 "Description", "Action");
2690 }
2691
2692 print_evsep();
2693 return (mdb_tgt_vespec_iter(mdb.m_target, print_event,
2694 (void *)(uintptr_t)opts));
2695 }
2696
2697 static int
tgt_status(const mdb_tgt_status_t * tsp)2698 tgt_status(const mdb_tgt_status_t *tsp)
2699 {
2700 const char *format;
2701 char buf[BUFSIZ];
2702
2703 if (tsp->st_flags & MDB_TGT_BUSY)
2704 return (DCMD_OK);
2705
2706 if (tsp->st_pc != 0) {
2707 if (mdb_dis_ins2str(mdb.m_disasm, mdb.m_target,
2708 MDB_TGT_AS_VIRT_I, buf, sizeof (buf), tsp->st_pc) !=
2709 tsp->st_pc)
2710 format = "target stopped at:\n%-#16a%8T%s\n";
2711 else
2712 format = "target stopped at %a:\n";
2713 mdb_warn(format, tsp->st_pc, buf);
2714 }
2715
2716 switch (tsp->st_state) {
2717 case MDB_TGT_IDLE:
2718 mdb_warn("target is idle\n");
2719 break;
2720 case MDB_TGT_RUNNING:
2721 if (tsp->st_flags & MDB_TGT_DSTOP)
2722 mdb_warn("target is running, stop directive pending\n");
2723 else
2724 mdb_warn("target is running\n");
2725 break;
2726 case MDB_TGT_STOPPED:
2727 if (tsp->st_pc == 0)
2728 mdb_warn("target is stopped\n");
2729 break;
2730 case MDB_TGT_UNDEAD:
2731 mdb_warn("target has terminated\n");
2732 break;
2733 case MDB_TGT_DEAD:
2734 mdb_warn("target is a core dump\n");
2735 break;
2736 case MDB_TGT_LOST:
2737 mdb_warn("target is no longer under debugger control\n");
2738 break;
2739 }
2740
2741 mdb_set_dot(tsp->st_pc);
2742 return (DCMD_OK);
2743 }
2744
2745 /*
2746 * mdb continue/step commands take an optional signal argument, but the
2747 * corresponding kmdb versions don't.
2748 */
2749 #ifdef _KMDB
2750 #define CONT_MAXARGS 0 /* no optional SIG argument */
2751 #else
2752 #define CONT_MAXARGS 1
2753 #endif
2754
2755 /*ARGSUSED*/
2756 static int
cmd_cont_common(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv,int (* t_cont)(mdb_tgt_t *,mdb_tgt_status_t *),const char * name)2757 cmd_cont_common(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
2758 int (*t_cont)(mdb_tgt_t *, mdb_tgt_status_t *), const char *name)
2759 {
2760 mdb_tgt_t *t = mdb.m_target;
2761 mdb_tgt_status_t st;
2762 int sig = 0;
2763
2764 if ((flags & DCMD_ADDRSPEC) || argc > CONT_MAXARGS)
2765 return (DCMD_USAGE);
2766
2767 if (argc > 0) {
2768 if (argv->a_type == MDB_TYPE_STRING) {
2769 if (proc_str2sig(argv->a_un.a_str, &sig) == -1) {
2770 mdb_warn("invalid signal name -- %s\n",
2771 argv->a_un.a_str);
2772 return (DCMD_USAGE);
2773 }
2774 } else
2775 sig = (int)(intmax_t)argv->a_un.a_val;
2776 }
2777
2778 (void) mdb_tgt_status(t, &st);
2779
2780 if (st.st_state == MDB_TGT_IDLE && mdb_tgt_run(t, 0, NULL) == -1) {
2781 if (errno != EMDB_TGT)
2782 mdb_warn("failed to create new target");
2783 return (DCMD_ERR);
2784 }
2785
2786 if (sig != 0 && mdb_tgt_signal(t, sig) == -1) {
2787 mdb_warn("failed to post signal %d", sig);
2788 return (DCMD_ERR);
2789 }
2790
2791 if (st.st_state == MDB_TGT_IDLE && t_cont == &mdb_tgt_step) {
2792 (void) mdb_tgt_status(t, &st);
2793 return (tgt_status(&st));
2794 }
2795
2796 if (t_cont(t, &st) == -1) {
2797 if (errno != EMDB_TGT)
2798 mdb_warn("failed to %s target", name);
2799 return (DCMD_ERR);
2800 }
2801
2802 return (tgt_status(&st));
2803 }
2804
2805 static int
cmd_step(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2806 cmd_step(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2807 {
2808 int (*func)(mdb_tgt_t *, mdb_tgt_status_t *) = &mdb_tgt_step;
2809 const char *name = "single-step";
2810
2811 if (argc > 0 && argv->a_type == MDB_TYPE_STRING) {
2812 if (strcmp(argv->a_un.a_str, "out") == 0) {
2813 func = &mdb_tgt_step_out;
2814 name = "step (out)";
2815 argv++;
2816 argc--;
2817 } else if (strcmp(argv->a_un.a_str, "over") == 0) {
2818 func = &mdb_tgt_next;
2819 name = "step (over)";
2820 argv++;
2821 argc--;
2822 }
2823 }
2824
2825 return (cmd_cont_common(addr, flags, argc, argv, func, name));
2826 }
2827
2828 static int
cmd_step_out(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2829 cmd_step_out(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2830 {
2831 return (cmd_cont_common(addr, flags, argc, argv,
2832 &mdb_tgt_step_out, "step (out)"));
2833 }
2834
2835 static int
cmd_next(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2836 cmd_next(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2837 {
2838 return (cmd_cont_common(addr, flags, argc, argv,
2839 &mdb_tgt_next, "step (over)"));
2840 }
2841
2842 static int
cmd_cont(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2843 cmd_cont(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2844 {
2845 return (cmd_cont_common(addr, flags, argc, argv,
2846 &mdb_tgt_continue, "continue"));
2847 }
2848
2849 #ifndef _KMDB
2850 /*ARGSUSED*/
2851 static int
cmd_run(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2852 cmd_run(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2853 {
2854 if (flags & DCMD_ADDRSPEC)
2855 return (DCMD_USAGE);
2856
2857 if (mdb_tgt_run(mdb.m_target, argc, argv) == -1) {
2858 if (errno != EMDB_TGT)
2859 mdb_warn("failed to create new target");
2860 return (DCMD_ERR);
2861 }
2862 return (cmd_cont(0, 0, 0, NULL));
2863 }
2864 #endif
2865
2866 /*
2867 * To simplify the implementation of :d, :z, and ::delete, we use the sp
2868 * parameter to store the criteria for what to delete. If spec_base is set,
2869 * we delete vespecs with a matching address. If spec_id is set, we delete
2870 * vespecs with a matching id. Otherwise, we delete all vespecs. We bump
2871 * sp->spec_size so the caller can tell how many vespecs were deleted.
2872 */
2873 static int
ve_delete(mdb_tgt_t * t,mdb_tgt_spec_desc_t * sp,int vid,void * data)2874 ve_delete(mdb_tgt_t *t, mdb_tgt_spec_desc_t *sp, int vid, void *data)
2875 {
2876 mdb_tgt_spec_desc_t spec;
2877 int status = -1;
2878
2879 if (vid < 0)
2880 return (0); /* skip over target implementation events */
2881
2882 if (sp->spec_base != 0) {
2883 (void) mdb_tgt_vespec_info(t, vid, &spec, NULL, 0);
2884 if (sp->spec_base - spec.spec_base < spec.spec_size)
2885 status = mdb_tgt_vespec_delete(t, vid);
2886 } else if (sp->spec_id == 0) {
2887 (void) mdb_tgt_vespec_info(t, vid, &spec, NULL, 0);
2888 if (!(spec.spec_flags & MDB_TGT_SPEC_STICKY))
2889 status = mdb_tgt_vespec_delete(t, vid);
2890 } else if (sp->spec_id == vid)
2891 status = mdb_tgt_vespec_delete(t, vid);
2892
2893 if (status == 0) {
2894 if (data != NULL)
2895 strfree(data);
2896 sp->spec_size++;
2897 }
2898
2899 return (0);
2900 }
2901
2902 static int
ve_delete_spec(mdb_tgt_spec_desc_t * sp)2903 ve_delete_spec(mdb_tgt_spec_desc_t *sp)
2904 {
2905 (void) mdb_tgt_vespec_iter(mdb.m_target,
2906 (mdb_tgt_vespec_f *)ve_delete, sp);
2907
2908 if (sp->spec_size == 0) {
2909 if (sp->spec_id != 0 || sp->spec_base != 0)
2910 mdb_warn("no traced events matched description\n");
2911 }
2912
2913 return (DCMD_OK);
2914 }
2915
2916 /*ARGSUSED*/
2917 static int
cmd_zapall(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2918 cmd_zapall(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2919 {
2920 mdb_tgt_spec_desc_t spec;
2921
2922 if ((flags & DCMD_ADDRSPEC) || argc != 0)
2923 return (DCMD_USAGE);
2924
2925 bzero(&spec, sizeof (spec));
2926 return (ve_delete_spec(&spec));
2927 }
2928
2929 static int
cmd_delete(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2930 cmd_delete(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2931 {
2932 mdb_tgt_spec_desc_t spec;
2933
2934 if (((flags & DCMD_ADDRSPEC) && argc > 0) || argc > 1)
2935 return (DCMD_USAGE);
2936
2937 bzero(&spec, sizeof (spec));
2938
2939 if (flags & DCMD_ADDRSPEC)
2940 spec.spec_base = addr;
2941 else if (argc == 0)
2942 spec.spec_base = mdb_get_dot();
2943 else if (argv->a_type == MDB_TYPE_STRING &&
2944 strcmp(argv->a_un.a_str, "all") != 0)
2945 spec.spec_id = (int)(intmax_t)mdb_strtonum(argv->a_un.a_str,
2946 10);
2947 else if (argv->a_type == MDB_TYPE_IMMEDIATE)
2948 spec.spec_id = (int)(intmax_t)argv->a_un.a_val;
2949
2950 return (ve_delete_spec(&spec));
2951 }
2952
2953 static int
cmd_write(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)2954 cmd_write(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2955 {
2956 mdb_tgt_as_t as;
2957 int rdback = mdb.m_flags & MDB_FL_READBACK;
2958 mdb_tgt_addr_t naddr;
2959 size_t forced_size = 0;
2960 boolean_t opt_p, opt_o, opt_l;
2961 uint64_t val = 0;
2962 int i;
2963
2964 opt_p = opt_o = opt_l = B_FALSE;
2965
2966 i = mdb_getopts(argc, argv,
2967 'p', MDB_OPT_SETBITS, B_TRUE, &opt_p,
2968 'o', MDB_OPT_SETBITS, B_TRUE, &opt_o,
2969 'l', MDB_OPT_UINTPTR_SET, &opt_l, (uintptr_t *)&forced_size, NULL);
2970
2971 if (!(flags & DCMD_ADDRSPEC))
2972 return (DCMD_USAGE);
2973
2974 if (opt_p && opt_o) {
2975 mdb_warn("-o and -p are incompatible\n");
2976 return (DCMD_USAGE);
2977 }
2978
2979 argc -= i;
2980 argv += i;
2981
2982 if (argc == 0)
2983 return (DCMD_USAGE);
2984
2985 switch (argv[0].a_type) {
2986 case MDB_TYPE_STRING:
2987 val = mdb_strtoull(argv[0].a_un.a_str);
2988 break;
2989 case MDB_TYPE_IMMEDIATE:
2990 val = argv[0].a_un.a_val;
2991 break;
2992 default:
2993 return (DCMD_USAGE);
2994 }
2995
2996 if (opt_p)
2997 as = MDB_TGT_AS_PHYS;
2998 else if (opt_o)
2999 as = MDB_TGT_AS_FILE;
3000 else
3001 as = MDB_TGT_AS_VIRT;
3002
3003 if (opt_l)
3004 naddr = write_var_uint(as, addr, val, forced_size, rdback);
3005 else
3006 naddr = write_ctf_uint(as, addr, val, rdback);
3007
3008 if (addr == naddr) {
3009 mdb_warn("failed to write %llr at address %#llx", val, addr);
3010 return (DCMD_ERR);
3011 }
3012
3013 return (DCMD_OK);
3014 }
3015
3016 void
write_help(void)3017 write_help(void)
3018 {
3019 mdb_printf(
3020 "-l length force a write with the specified length in bytes\n"
3021 "-o write data to the object file location specified\n"
3022 "-p write data to the physical address specified\n"
3023 "\n"
3024 "Attempts to write the given value to the address provided.\n"
3025 "If -l is not specified, the address must be the position of a\n"
3026 "symbol that is either of integer, pointer, or enum type. The\n"
3027 "type and the size of the symbol are inferred by the CTF found\n"
3028 "in the provided address. The length of the write is guaranteed\n"
3029 "to be the inferred size of the symbol.\n"
3030 "\n"
3031 "If no CTF data exists, or the address provided is not a symbol\n"
3032 "of integer or pointer type, then the write fails. At that point\n"
3033 "the user can force the write by using the '-l' option and\n"
3034 "specifying its length.\n"
3035 "\n"
3036 "Note that forced writes with a length that are bigger than\n"
3037 "the size of the biggest data pointer supported are not allowed."
3038 "\n");
3039 }
3040
3041 static void
srcexec_file_help(void)3042 srcexec_file_help(void)
3043 {
3044 mdb_printf(
3045 "The library of macros delivered with previous versions of Solaris have been\n"
3046 "superseded by the dcmds and walkers provided by MDB. See ::help for\n"
3047 "commands that can be used to list the available dcmds and walkers.\n"
3048 "\n"
3049 "Aliases have been created for several of the more popular macros. To see\n"
3050 "the list of aliased macros, as well as their native MDB equivalents,\n"
3051 "type $M.\n");
3052
3053 #ifdef _KMDB
3054 mdb_printf(
3055 "When invoked, the $< and $<< dcmds will consult the macro alias list. If an\n"
3056 "alias cannot be found, an attempt will be made to locate a data type whose\n"
3057 "name corresponds to the requested macro. If such a type can be found, it\n"
3058 "will be displayed using the ::print dcmd.\n");
3059 #else
3060 mdb_printf(
3061 "When invoked, the $< and $<< dcmds will first attempt to locate a macro with\n"
3062 "the indicated name. If no macro can be found, and if no alias exists for\n"
3063 "this macro, an attempt will be made to locate a data type whose name\n"
3064 "corresponds to the requested macro. If such a type can be found, it will be\n"
3065 "displayed using the ::print dcmd.\n");
3066 #endif
3067 }
3068
3069 static void
events_help(void)3070 events_help(void)
3071 {
3072 mdb_printf("Options:\n"
3073 "-a show all events, including internal debugger events\n"
3074 "-v show verbose display, including inactivity reason\n"
3075 "\nOutput Columns:\n"
3076 "ID decimal event specifier id number:\n"
3077 " [ ] event tracing is enabled\n"
3078 " ( ) event tracing is disabled\n"
3079 " < > target is currently stopped on this type of event\n\n"
3080 "S event specifier state:\n"
3081 " - event specifier is idle (not applicable yet)\n"
3082 " + event specifier is active\n"
3083 " * event specifier is armed (target program running)\n"
3084 " ! error occurred while attempting to arm event\n\n"
3085 "TA event specifier flags:\n"
3086 " t event specifier is temporary (delete at next stop)\n"
3087 " T event specifier is sticky (::delete all has no effect)\n"
3088 " d event specifier will be disabled when HT = LM\n"
3089 " D event specifier will be deleted when HT = LM\n"
3090 " s target will automatically stop when HT = LM\n\n"
3091 "HT hit count (number of times event has occurred)\n"
3092 "LM hit limit (limit for autostop, disable, delete)\n");
3093 }
3094
3095 static void
dump_help(void)3096 dump_help(void)
3097 {
3098 mdb_printf(
3099 "-e adjust for endianness\n"
3100 " (assumes 4-byte words; use -g to change word size)\n"
3101 #ifdef _KMDB
3102 "-f no effect\n"
3103 #else
3104 "-f dump from object file\n"
3105 #endif
3106 "-g n display bytes in groups of n\n"
3107 " (default is 4; n must be a power of 2, divide line width)\n"
3108 "-l n display n bytes\n"
3109 " (default is 1; rounded up to multiple of line width)\n"
3110 "-p dump from physical memory\n"
3111 "-q don't print ASCII\n"
3112 "-r use relative numbering (automatically sets -u)\n"
3113 "-s elide repeated lines\n"
3114 "-t only read from and display contents of specified addresses\n"
3115 " (default is to read and print entire lines)\n"
3116 "-u un-align output\n"
3117 " (default is to align output at paragraph boundary)\n"
3118 "-w n display n 16-byte paragraphs per line\n"
3119 " (default is 1, maximum is 16)\n");
3120 }
3121
3122 static void
bitx_help(void)3123 bitx_help(void)
3124 {
3125 mdb_printf(
3126 "Extract bits from an integer or, with the optional third\n"
3127 "argument, set the value in the provided bit range and show\n"
3128 "the result.\n\n"
3129 "Bit positions are inclusive and specified with the high bit\n"
3130 "first.\n");
3131 }
3132
3133 static int
cmd_bitx(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)3134 cmd_bitx(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3135 {
3136 uint64_t val = addr;
3137 uint8_t high, low;
3138
3139 if (!(flags & DCMD_ADDRSPEC))
3140 return (DCMD_USAGE);
3141
3142 if (argc != 2 && argc != 3)
3143 return (DCMD_USAGE);
3144
3145 high = (uint8_t)mdb_argtoull(&argv[0]);
3146 low = (uint8_t)mdb_argtoull(&argv[1]);
3147
3148 if (high > 63 || low > 63) {
3149 mdb_warn("bit positions must be in the range [0,%r]\n", 63);
3150 return (DCMD_ERR);
3151 }
3152
3153 if (high < low) {
3154 mdb_warn("high bit must not be less than the low bit\n");
3155 return (DCMD_ERR);
3156 }
3157
3158 uint64_t mask = (1ULL << (high - low + 1)) - 1ULL;
3159
3160 if (argc == 3) {
3161 uint64_t nval = (uint64_t)mdb_argtoull(&argv[2]);
3162
3163 if ((~mask & nval) != 0) {
3164 mdb_warn(
3165 "value (%lr) too large for bit range [%r:%r]\n",
3166 nval, high, low);
3167 return (DCMD_ERR);
3168 }
3169
3170 val &= ~(mask << low);
3171 val |= nval << low;
3172
3173 mdb_nv_set_value(mdb.m_dot, val);
3174 } else {
3175 val = ((val >> low) & mask);
3176 }
3177 mdb_printf("%lr\n", val);
3178
3179 return (DCMD_OK);
3180 }
3181
3182 /* "DESCRIPTION" section text for ::bcmp */
3183 static void
bcmp_help(void)3184 bcmp_help(void)
3185 {
3186 mdb_printf(
3187 "-n Number of bytes to compare\n"
3188 "\n"
3189 "Both addr2 and -n arguments are required.\n"
3190 "\n"
3191 "Output is silent if both memory areas are identical. If they\n"
3192 "are not identical, the offset of the first differing byte prints\n"
3193 "on stdout or a pipe and dot is set to (addr + the offset).\n");
3194 }
3195
3196 /* Actual implementation of ::bcmp */
3197 static int
cmd_bcmp(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)3198 cmd_bcmp(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
3199 {
3200 uintptr_t other_addr = 0;
3201 uint64_t length = 0;
3202 uintptr_t high_addr, low_addr;
3203
3204 if (argc < 1 || argc > 3 || !(flags & DCMD_ADDRSPEC) ||
3205 argv[0].a_type != MDB_TYPE_STRING || argv[0].a_un.a_str[0] == '-') {
3206 return (DCMD_USAGE);
3207 }
3208
3209 if (dis_str2addr(argv[0].a_un.a_str, &other_addr) == -1)
3210 return (DCMD_ERR);
3211 argv++;
3212 argc--;
3213
3214 if (mdb_getopts(argc, argv, 'n', MDB_OPT_UINT64, &length, NULL) !=
3215 argc) {
3216 return (DCMD_USAGE);
3217 }
3218
3219 /* Cap the length to (2 ^ 31 - 1) bytes for now. */
3220 if (length > INT_MAX || length == 0) {
3221 mdb_warn("Lengths not [1..0t%d]/[1..0x%x] are unsupported.\n",
3222 INT_MAX, INT_MAX);
3223 return (DCMD_ERR);
3224 }
3225
3226 if (addr > other_addr) {
3227 high_addr = addr;
3228 low_addr = other_addr;
3229 } else {
3230 high_addr = other_addr;
3231 low_addr = addr;
3232 }
3233
3234 if (high_addr - low_addr < length) {
3235 mdb_warn("Segments are overlapping for length %r.\n", length);
3236 return (DCMD_ERR);
3237 }
3238
3239 /* Check for bounds at the higher address, which means wrapping? */
3240 if (high_addr + length < high_addr) {
3241 mdb_warn("High-address segment would wrap around\n");
3242 return (DCMD_ERR);
3243 }
3244
3245 /*
3246 * Okay, we can finally start the comparison. Be deliberate, we can
3247 * make this faster later.
3248 *
3249 * For now, read them in 1KiB at a time.
3250 */
3251 uintptr_t offset = 0;
3252 #define BCMP_CHUNK_SIZE 1024
3253 uint8_t *chunk = mdb_alloc(BCMP_CHUNK_SIZE, UM_SLEEP | UM_GC);
3254 uint8_t *other_chunk = mdb_alloc(BCMP_CHUNK_SIZE, UM_SLEEP | UM_GC);
3255 while (offset < length) {
3256 size_t readsize = (length - offset > BCMP_CHUNK_SIZE) ?
3257 BCMP_CHUNK_SIZE : length - offset;
3258 size_t chunk_offset = 0;
3259
3260 if (mdb_vread(chunk, readsize, addr + offset) == -1) {
3261 mdb_warn("Read failure 0x%p, length %d\n",
3262 addr + offset, readsize);
3263 return (DCMD_ERR);
3264 }
3265 if (mdb_vread(other_chunk, readsize, other_addr + offset) ==
3266 -1) {
3267 mdb_warn("Read failure 0x%p, length %d\n",
3268 other_addr + offset, readsize);
3269 return (DCMD_ERR);
3270 }
3271 /* Like I said, we can make this faster later. */
3272 while (chunk_offset < readsize) {
3273 if (chunk[chunk_offset] != other_chunk[chunk_offset])
3274 break;
3275 chunk_offset++;
3276 }
3277 offset += chunk_offset;
3278 if (chunk_offset < readsize) {
3279 mdb_printf("%lr\n", offset);
3280 /* Set the dot so someone can utter "/B" immediately. */
3281 mdb_set_dot(addr + offset);
3282 break; /* out of while loop, to return DCMD_OK... */
3283 }
3284 }
3285 #undef BCMP_CHUNK_SIZE
3286
3287 return (DCMD_OK);
3288 }
3289
3290 /*
3291 * Table of built-in dcmds associated with the root 'mdb' module. Future
3292 * expansion of this program should be done here, or through the external
3293 * loadable module interface.
3294 */
3295 const mdb_dcmd_t mdb_dcmd_builtins[] = {
3296
3297 /*
3298 * dcmds common to both mdb and kmdb
3299 */
3300 { ">", "variable-name", "assign variable", cmd_assign_variable },
3301 { "/", "fmt-list", "format data from virtual as", cmd_print_core },
3302 { "\\", "fmt-list", "format data from physical as", cmd_print_phys },
3303 { "@", "fmt-list", "format data from physical as", cmd_print_phys },
3304 { "=", "fmt-list", "format immediate value", cmd_print_value },
3305 { "$<", "macro-name", "replace input with macro",
3306 cmd_exec_file, srcexec_file_help },
3307 { "$<<", "macro-name", "source macro",
3308 cmd_src_file, srcexec_file_help},
3309 { "$%", NULL, NULL, cmd_quit },
3310 { "$?", NULL, "print status and registers", cmd_notsup },
3311 { "$a", NULL, NULL, cmd_algol },
3312 { "$b", "[-av]", "list traced software events",
3313 cmd_events, events_help },
3314 { "$c", "?[cnt]", "print stack backtrace", cmd_notsup },
3315 { "$C", "?[cnt]", "print stack backtrace", cmd_notsup },
3316 { "$d", NULL, "get/set default output radix", cmd_radix },
3317 { "$D", "?[mode,...]", NULL, cmd_dbmode },
3318 { "$e", NULL, "print listing of global symbols", cmd_globals },
3319 { "$f", NULL, "print listing of source files", cmd_files },
3320 { "$m", "?[name]", "print address space mappings", cmd_mappings },
3321 { "$M", NULL, "list macro aliases", cmd_macalias_list },
3322 { "$P", "[prompt]", "set debugger prompt string", cmd_prompt },
3323 { "$q", NULL, "quit debugger", cmd_quit },
3324 { "$Q", NULL, "quit debugger", cmd_quit },
3325 { "$r", NULL, "print general-purpose registers", cmd_notsup },
3326 { "$s", NULL, "get/set symbol matching distance", cmd_symdist },
3327 { "$v", NULL, "print non-zero variables", cmd_nzvars },
3328 { "$V", "[mode]", "get/set disassembly mode", cmd_dismode },
3329 { "$w", NULL, "get/set output page width", cmd_pgwidth },
3330 { "$W", NULL, "re-open target in write mode", cmd_reopen },
3331 { ":a", ":[cmd...]", "set read access watchpoint", cmd_oldwpr },
3332 { ":b", ":[cmd...]", "breakpoint at the specified address", cmd_oldbp },
3333 { ":d", "?[id|all]", "delete traced software events", cmd_delete },
3334 { ":p", ":[cmd...]", "set execute access watchpoint", cmd_oldwpx },
3335 { ":S", NULL, NULL, cmd_step },
3336 { ":w", ":[cmd...]", "set write access watchpoint", cmd_oldwpw },
3337 { ":z", NULL, "delete all traced software events", cmd_zapall },
3338 { "array", ":[type count] [variable]", "print each array element's "
3339 "address", cmd_array },
3340 { "bitx", ":<high bit> <low bit> [new value]",
3341 "extract bits from, or set bits in, a value", cmd_bitx, bitx_help },
3342 { "bcmp", ":<addr2> <-n count>",
3343 "compare two disjoint sections of memory", cmd_bcmp, bcmp_help},
3344 { "bp", "?[+/-dDestT] [-c cmd] [-n count] sym ...", "breakpoint at the "
3345 "specified addresses or symbols", cmd_bp, bp_help },
3346 { "dcmds", "[[-n] pattern]",
3347 "list available debugger commands", cmd_dcmds, cmd_dcmds_help },
3348 { "delete", "?[id|all]", "delete traced software events", cmd_delete },
3349 { "dis", "?[-abfpw] [-n cnt] [addr]", "disassemble near addr", cmd_dis,
3350 dis_help },
3351 { "disasms", NULL, "list available disassemblers", cmd_disasms },
3352 { "dismode", "[mode]", "get/set disassembly mode", cmd_dismode },
3353 { "dmods", "[-l] [mod]", "list loaded debugger modules", cmd_dmods },
3354 { "dump", "?[-eqrstu] [-f|-p] [-g bytes] [-l bytes] [-w paragraphs]",
3355 "dump memory from specified address", cmd_dump, dump_help },
3356 { "echo", "args ...", "echo arguments", cmd_echo },
3357 { "enum", "?[-ex] enum [name]", "print an enumeration", cmd_enum,
3358 enum_help },
3359 { "eval", "command", "evaluate the specified command", cmd_eval },
3360 { "events", "[-av]", "list traced software events",
3361 cmd_events, events_help },
3362 { "evset", "?[+/-dDestT] [-c cmd] [-n count] id ...",
3363 "set software event specifier attributes", cmd_evset, evset_help },
3364 { "files", "[object]", "print listing of source files", cmd_files },
3365 #ifdef __sparc
3366 { "findsym", "?[-g] [symbol|addr ...]", "search for symbol references "
3367 "in all known functions", cmd_findsym, NULL },
3368 #endif
3369 { "formats", NULL, "list format specifiers", cmd_formats },
3370 { "grep", "?expr", "print dot if expression is true", cmd_grep },
3371 { "head", "-num|-n num", "limit number of elements in pipe", cmd_head,
3372 head_help },
3373 { "help", "[cmd]", "list commands/command help", cmd_help, NULL,
3374 cmd_help_tab },
3375 { "linkerset", "[name]", "display linkersets", cmd_linkerset,
3376 linkerset_help, cmd_linkerset_tab },
3377 { "list", "?type member [variable]",
3378 "walk list using member as link pointer", cmd_list, NULL,
3379 mdb_tab_complete_mt },
3380 { "map", "?expr", "print dot after evaluating expression", cmd_map },
3381 { "mappings", "?[name]", "print address space mappings", cmd_mappings },
3382 { "nm", "?[-DPdghnopuvx] [-f format] [-t types] [object]",
3383 "print symbols", cmd_nm, nm_help },
3384 { "nmadd", ":[-fo] [-e end] [-s size] name",
3385 "add name to private symbol table", cmd_nmadd, nmadd_help },
3386 { "nmdel", "name", "remove name from private symbol table", cmd_nmdel },
3387 { "obey", NULL, NULL, cmd_obey },
3388 { "objects", "[-v]", "print load objects information", cmd_objects },
3389 { "offsetof", "type member", "print the offset of a given struct "
3390 "or union member", cmd_offsetof, NULL, mdb_tab_complete_mt },
3391 { "print", "?[-aCdhiLptx] [-c lim] [-l lim] [type] [member|offset ...]",
3392 "print the contents of a data structure", cmd_print, print_help,
3393 cmd_print_tab },
3394 { "printf", "?format type member ...", "print and format the "
3395 "member(s) of a data structure", cmd_printf, printf_help,
3396 cmd_printf_tab },
3397 { "regs", NULL, "print general purpose registers", cmd_notsup },
3398 { "set", "[-wF] [+/-o opt] [-s dist] [-I path] [-L path] [-P prompt]",
3399 "get/set debugger properties", cmd_set },
3400 { "showrev", "[-pv]", "print version information", cmd_showrev },
3401 { "sizeof", "type", "print the size of a type", cmd_sizeof, NULL,
3402 cmd_sizeof_tab },
3403 { "stack", "?[cnt]", "print stack backtrace", cmd_notsup },
3404 { "stackregs", "?", "print stack backtrace and registers",
3405 cmd_notsup },
3406 { "status", NULL, "print summary of current target", cmd_notsup },
3407 { "term", NULL, "display current terminal type", cmd_term },
3408 { "typeset", "[+/-t] var ...", "set variable attributes", cmd_typeset },
3409 { "typedef", "[-c model | -d | -l | -r file | -w file ] [type] [name]",
3410 "create synthetic types", cmd_typedef, cmd_typedef_help },
3411 { "typelist", NULL, "list known types", cmd_typelist },
3412 { "unset", "[name ...]", "unset variables", cmd_unset },
3413 { "vars", "[-npt]", "print listing of variables", cmd_vars },
3414 { "version", NULL, "print debugger version string", cmd_version },
3415 { "vtop", ":[-a as]", "print physical mapping of virtual address",
3416 cmd_vtop },
3417 { "walk", "?name [variable]", "walk data structure", cmd_walk, NULL,
3418 cmd_walk_tab },
3419 { "walkers", "[[-n] pattern]", "list available walkers",
3420 cmd_walkers, cmd_walkers_help },
3421 { "whatis", ":[-aikqv]", "given an address, return information",
3422 cmd_whatis, whatis_help },
3423 { "whence", "[-v] name ...", "show source of walk or dcmd", cmd_which },
3424 { "which", "[-v] name ...", "show source of walk or dcmd", cmd_which },
3425 { "write", "?[-op] [-l len] value",
3426 "write value to the provided memory location", cmd_write,
3427 write_help },
3428 { "xdata", NULL, "print list of external data buffers", cmd_xdata },
3429
3430 #ifdef _KMDB
3431 /*
3432 * dcmds specific to kmdb, or which have kmdb-specific arguments
3433 */
3434 { "?", "fmt-list", "format data from virtual as", cmd_print_core },
3435 { ":c", NULL, "continue target execution", cmd_cont },
3436 { ":e", NULL, "step target over next instruction", cmd_next },
3437 { ":s", NULL, "single-step target to next instruction", cmd_step },
3438 { ":u", NULL, "step target out of current function", cmd_step_out },
3439 { "cont", NULL, "continue target execution", cmd_cont },
3440 { "load", "[-sd] module", "load debugger module", cmd_load, load_help },
3441 { "next", NULL, "step target over next instruction", cmd_next },
3442 { "quit", "[-u]", "quit debugger", cmd_quit, quit_help },
3443 { "step", "[ over | out ]",
3444 "single-step target to next instruction", cmd_step },
3445 { "unload", "[-d] module", "unload debugger module", cmd_unload,
3446 unload_help },
3447 { "wp", ":[+/-dDelstT] [-rwx] [-pi] [-c cmd] [-n count] [-L size]",
3448 "set a watchpoint at the specified address", cmd_wp, wp_help },
3449
3450 #else
3451 /*
3452 * dcmds specific to mdb, or which have mdb-specific arguments
3453 */
3454 { "?", "fmt-list", "format data from object file", cmd_print_object },
3455 { "$>", "[file]", "log session to a file", cmd_old_log },
3456 { "$g", "?", "get/set demangling options", cmd_demflags },
3457 { "$G", NULL, "enable/disable demangling support", cmd_demangle },
3458 { "$i", NULL, "print signals that are ignored", cmd_notsup },
3459 { "$l", NULL, "print the representative thread's lwp id", cmd_notsup },
3460 { "$p", ":", "change debugger target context", cmd_context },
3461 { "$x", NULL, "print floating point registers", cmd_notsup },
3462 { "$X", NULL, "print floating point registers", cmd_notsup },
3463 { "$y", NULL, "print floating point registers", cmd_notsup },
3464 { "$Y", NULL, "print floating point registers", cmd_notsup },
3465 { ":A", "?[core|pid]", "attach to process or core file", cmd_notsup },
3466 { ":c", "[SIG]", "continue target execution", cmd_cont },
3467 { ":e", "[SIG]", "step target over next instruction", cmd_next },
3468 { ":i", ":", "ignore signal (delete all matching events)", cmd_notsup },
3469 { ":k", NULL, "forcibly kill and release target", cmd_notsup },
3470 { ":t", "?[+/-dDestT] [-c cmd] [-n count] SIG ...", "stop on delivery "
3471 "of the specified signals", cmd_sigbp, sigbp_help },
3472 { ":r", "[ args ... ]", "run a new target process", cmd_run },
3473 { ":R", NULL, "release the previously attached process", cmd_notsup },
3474 { ":s", "[SIG]", "single-step target to next instruction", cmd_step },
3475 { ":u", "[SIG]", "step target out of current function", cmd_step_out },
3476 { "attach", "?[core|pid]",
3477 "attach to process or core file", cmd_notsup },
3478 { "cat", "[file ...]", "concatenate and display files", cmd_cat },
3479 { "cont", "[SIG]", "continue target execution", cmd_cont },
3480 { "context", ":", "change debugger target context", cmd_context },
3481 { "dem", "name ...", "demangle C++ symbol names", cmd_demstr },
3482 { "fltbp", "?[+/-dDestT] [-c cmd] [-n count] fault ...",
3483 "stop on machine fault", cmd_fltbp, fltbp_help },
3484 { "fpregs", NULL, "print floating point registers", cmd_notsup },
3485 { "kill", NULL, "forcibly kill and release target", cmd_notsup },
3486 { "load", "[-s] module", "load debugger module", cmd_load, load_help },
3487 { "log", "[-d | [-e] file]", "log session to a file", cmd_log },
3488 { "next", "[SIG]", "step target over next instruction", cmd_next },
3489 { "quit", NULL, "quit debugger", cmd_quit },
3490 { "release", NULL,
3491 "release the previously attached process", cmd_notsup },
3492 { "run", "[ args ... ]", "run a new target process", cmd_run },
3493 { "sigbp", "?[+/-dDestT] [-c cmd] [-n count] SIG ...", "stop on "
3494 "delivery of the specified signals", cmd_sigbp, sigbp_help },
3495 { "step", "[ over | out ] [SIG]",
3496 "single-step target to next instruction", cmd_step },
3497 { "sysbp", "?[+/-dDestT] [-io] [-c cmd] [-n count] syscall ...",
3498 "stop on entry or exit from system call", cmd_sysbp, sysbp_help },
3499 { "unload", "module", "unload debugger module", cmd_unload },
3500 { "wp", ":[+/-dDelstT] [-rwx] [-c cmd] [-n count] [-L size]",
3501 "set a watchpoint at the specified address", cmd_wp, wp_help },
3502 #endif
3503
3504 { NULL }
3505 };
3506