xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_nm.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <sys/elf.h>
27 #include <sys/elf_SPARC.h>
28 
29 #include <libproc.h>
30 #include <libctf.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 
36 #include <mdb/mdb_string.h>
37 #include <mdb/mdb_argvec.h>
38 #include <mdb/mdb_nv.h>
39 #include <mdb/mdb_fmt.h>
40 #include <mdb/mdb_target.h>
41 #include <mdb/mdb_err.h>
42 #include <mdb/mdb_debug.h>
43 #include <mdb/mdb_conf.h>
44 #include <mdb/mdb_module.h>
45 #include <mdb/mdb_modapi.h>
46 #include <mdb/mdb_stdlib.h>
47 #include <mdb/mdb_lex.h>
48 #include <mdb/mdb_io_impl.h>
49 #include <mdb/mdb_help.h>
50 #include <mdb/mdb_disasm.h>
51 #include <mdb/mdb_frame.h>
52 #include <mdb/mdb_evset.h>
53 #include <mdb/mdb_print.h>
54 #include <mdb/mdb_nm.h>
55 #include <mdb/mdb_set.h>
56 #include <mdb/mdb_demangle.h>
57 #include <mdb/mdb.h>
58 
59 enum {
60 	NM_FMT_INDEX	= 0x0001,			/* -f ndx */
61 	NM_FMT_VALUE	= 0x0002,			/* -f val */
62 	NM_FMT_SIZE	= 0x0004,			/* -f size */
63 	NM_FMT_TYPE	= 0x0008,			/* -f type */
64 	NM_FMT_BIND	= 0x0010,			/* -f bind */
65 	NM_FMT_OTHER	= 0x0020,			/* -f oth */
66 	NM_FMT_SHNDX	= 0x0040,			/* -f shndx */
67 	NM_FMT_NAME	= 0x0080,			/* -f name */
68 	NM_FMT_CTYPE	= 0x0100,			/* -f ctype */
69 	NM_FMT_OBJECT	= 0x0200,			/* -f obj */
70 
71 	NM_FMT_CTFID	= 0x1000			/* -f ctfid */
72 };
73 
74 enum {
75 	NM_TYPE_NOTY	= 1 << STT_NOTYPE,		/* -t noty */
76 	NM_TYPE_OBJT	= 1 << STT_OBJECT,		/* -t objt */
77 	NM_TYPE_FUNC	= 1 << STT_FUNC,		/* -t func */
78 	NM_TYPE_SECT	= 1 << STT_SECTION,		/* -t sect */
79 	NM_TYPE_FILE	= 1 << STT_FILE,		/* -t file */
80 	NM_TYPE_COMM	= 1 << STT_COMMON,		/* -t comm */
81 	NM_TYPE_TLS	= 1 << STT_TLS,			/* -t tls */
82 	NM_TYPE_REGI	= 1 << STT_SPARC_REGISTER	/* -t regi */
83 };
84 
85 typedef struct {
86 	GElf_Sym nm_sym;
87 	const char *nm_name;
88 	mdb_syminfo_t nm_si;
89 	const char *nm_object;
90 	ctf_file_t *nm_fp;
91 } nm_sym_t;
92 
93 typedef struct {
94 	ctf_file_t *nii_fp;
95 
96 	uint_t nii_flags;
97 	uint_t nii_types;
98 	ulong_t nii_id;
99 	const char *nii_pfmt;
100 	const char *nii_ofmt;
101 
102 	const GElf_Sym *nii_symp;
103 
104 	nm_sym_t **nii_sympp;
105 } nm_iter_info_t;
106 
107 typedef struct {
108 	mdb_tgt_sym_f *ngs_cb;
109 	void *ngs_arg;
110 	mdb_syminfo_t ngs_si;
111 	const char *ngs_object;
112 } nm_gelf_symtab_t;
113 
114 typedef struct {
115 	uint_t noi_which;
116 	uint_t noi_type;
117 	mdb_tgt_sym_f *noi_cb;
118 	nm_iter_info_t *noi_niip;
119 } nm_object_iter_t;
120 
121 static const char *
122 nm_type2str(uchar_t info)
123 {
124 	switch (GELF_ST_TYPE(info)) {
125 	case STT_NOTYPE:
126 		return ("NOTY");
127 	case STT_OBJECT:
128 		return ("OBJT");
129 	case STT_FUNC:
130 		return ("FUNC");
131 	case STT_SECTION:
132 		return ("SECT");
133 	case STT_FILE:
134 		return ("FILE");
135 	case STT_COMMON:
136 		return ("COMM");
137 	case STT_TLS:
138 		return ("TLS");
139 	case STT_SPARC_REGISTER:
140 		return ("REGI");
141 	default:
142 		return ("?");
143 	}
144 }
145 
146 static const char *
147 nm_bind2str(uchar_t info)
148 {
149 	switch (GELF_ST_BIND(info)) {
150 	case STB_LOCAL:
151 		return ("LOCL");
152 	case STB_GLOBAL:
153 		return ("GLOB");
154 	case STB_WEAK:
155 		return ("WEAK");
156 	default:
157 		return ("?");
158 	}
159 }
160 
161 static const char *
162 nm_sect2str(GElf_Half shndx)
163 {
164 	static char buf[16];
165 
166 	switch (shndx) {
167 	case SHN_UNDEF:
168 		return ("UNDEF");
169 	case SHN_ABS:
170 		return ("ABS");
171 	case SHN_COMMON:
172 		return ("COMMON");
173 	default:
174 		(void) mdb_iob_snprintf(buf, sizeof (buf), "%hu", shndx);
175 		return (buf);
176 	}
177 }
178 
179 static char *
180 nm_func_signature(ctf_file_t *fp, uint_t index, char *buf, size_t len)
181 {
182 	int n;
183 	ctf_funcinfo_t f;
184 	ctf_id_t argv[32];
185 	char arg[32];
186 	char *start = buf;
187 	char *sep = "";
188 	int i;
189 
190 	if (ctf_func_info(fp, index, &f) == CTF_ERR)
191 		return (NULL);
192 
193 	if (ctf_type_name(fp, f.ctc_return, arg, sizeof (arg)) != NULL)
194 		n = mdb_snprintf(buf, len, "%s (*)(", arg);
195 	else
196 		n = mdb_snprintf(buf, len, "<%ld> (*)(", f.ctc_return);
197 
198 	if (len <= n)
199 		return (start);
200 
201 	buf += n;
202 	len -= n;
203 
204 	(void) ctf_func_args(fp, index, sizeof (argv) / sizeof (argv[0]), argv);
205 
206 	for (i = 0; i < f.ctc_argc; i++) {
207 		if (ctf_type_name(fp, argv[i], arg, sizeof (arg)) != NULL)
208 			n = mdb_snprintf(buf, len, "%s%s", sep, arg);
209 		else
210 			n = mdb_snprintf(buf, len, "%s<%ld>", sep, argv[i]);
211 
212 		if (len <= n)
213 			return (start);
214 
215 		buf += n;
216 		len -= n;
217 
218 		sep = ", ";
219 	}
220 
221 	if (f.ctc_flags & CTF_FUNC_VARARG) {
222 		n = mdb_snprintf(buf, len, "%s...", sep);
223 		if (len <= n)
224 			return (start);
225 		buf += n;
226 		len -= n;
227 	} else if (f.ctc_argc == 0) {
228 		n = mdb_snprintf(buf, len, "void");
229 		if (len <= n)
230 			return (start);
231 		buf += n;
232 		len -= n;
233 	}
234 
235 	(void) mdb_snprintf(buf, len, ")");
236 
237 	return (start);
238 }
239 
240 static void
241 nm_print_ctype(void *data)
242 {
243 	nm_iter_info_t *niip = data;
244 	char buf[256];
245 	ctf_id_t id;
246 	char *str = NULL;
247 	uint_t index = niip->nii_id;
248 	ctf_file_t *fp = niip->nii_fp;
249 
250 	if (fp != NULL) {
251 		if (GELF_ST_TYPE(niip->nii_symp->st_info) == STT_FUNC)
252 			str = nm_func_signature(fp, index, buf, sizeof (buf));
253 		else if ((id = ctf_lookup_by_symbol(fp, index)) != CTF_ERR)
254 			str = ctf_type_name(fp, id, buf, sizeof (buf));
255 	}
256 
257 	if (str == NULL)
258 		str = "<unknown type>";
259 
260 	mdb_printf("%-50s", str);
261 }
262 
263 static void
264 nm_print_ctfid(void *data)
265 {
266 	nm_iter_info_t *niip = data;
267 	ctf_id_t id;
268 	uint_t index = niip->nii_id;
269 	ctf_file_t *fp = niip->nii_fp;
270 
271 	if (fp != NULL && (id = ctf_lookup_by_symbol(fp, index)) != CTF_ERR) {
272 		mdb_printf("%-9ld", id);
273 	} else {
274 		mdb_printf("%9s", "");
275 	}
276 }
277 
278 static void
279 nm_print_obj(void *data)
280 {
281 	const char *obj = (const char *)data;
282 
283 	if (obj == MDB_TGT_OBJ_EXEC)
284 		obj = "exec";
285 	else if (obj == MDB_TGT_OBJ_RTLD)
286 		obj = "rtld";
287 	else if (obj == MDB_TGT_OBJ_EVERY)
288 		obj = "";
289 
290 	mdb_printf("%-15s", obj);
291 }
292 
293 /*ARGSUSED*/
294 static int
295 nm_print(void *data, const GElf_Sym *sym, const char *name,
296     const mdb_syminfo_t *sip, const char *obj)
297 {
298 	nm_iter_info_t *niip = data;
299 
300 	if (!((1 << GELF_ST_TYPE(sym->st_info)) & niip->nii_types))
301 		return (0);
302 
303 	niip->nii_id = sip->sym_id;
304 	niip->nii_symp = sym;
305 
306 	mdb_table_print(niip->nii_flags, "|",
307 	    MDB_TBL_PRNT, NM_FMT_INDEX, "%5u", sip->sym_id,
308 	    MDB_TBL_FUNC, NM_FMT_OBJECT, nm_print_obj, obj,
309 	    MDB_TBL_PRNT, NM_FMT_VALUE, niip->nii_pfmt, sym->st_value,
310 	    MDB_TBL_PRNT, NM_FMT_SIZE, niip->nii_pfmt, sym->st_size,
311 	    MDB_TBL_PRNT, NM_FMT_TYPE, "%-5s", nm_type2str(sym->st_info),
312 	    MDB_TBL_PRNT, NM_FMT_BIND, "%-5s", nm_bind2str(sym->st_info),
313 	    MDB_TBL_PRNT, NM_FMT_OTHER, niip->nii_ofmt, sym->st_other,
314 	    MDB_TBL_PRNT, NM_FMT_SHNDX, "%-8s", nm_sect2str(sym->st_shndx),
315 	    MDB_TBL_FUNC, NM_FMT_CTFID, nm_print_ctfid, niip,
316 	    MDB_TBL_FUNC, NM_FMT_CTYPE, nm_print_ctype, niip,
317 	    MDB_TBL_PRNT, NM_FMT_NAME, "%s", name,
318 	    MDB_TBL_DONE);
319 
320 	mdb_printf("\n");
321 
322 	return (0);
323 }
324 
325 /*ARGSUSED*/
326 static int
327 nm_any(void *data, const GElf_Sym *sym, const char *name,
328     const mdb_syminfo_t *sip, const char *obj)
329 {
330 	return (nm_print(data, sym, name, sip, obj));
331 }
332 
333 /*ARGSUSED*/
334 static int
335 nm_undef(void *data, const GElf_Sym *sym, const char *name,
336     const mdb_syminfo_t *sip, const char *obj)
337 {
338 	if (sym->st_shndx == SHN_UNDEF)
339 		return (nm_print(data, sym, name, sip, obj));
340 
341 	return (0);
342 }
343 
344 /*ARGSUSED*/
345 static int
346 nm_asgn(void *data, const GElf_Sym *sym, const char *name,
347     const mdb_syminfo_t *sip, const char *obj)
348 {
349 	const char *opts;
350 
351 	switch (GELF_ST_TYPE(sym->st_info)) {
352 	case STT_FUNC:
353 		opts = "-f";
354 		break;
355 	case STT_OBJECT:
356 		opts = "-o";
357 		break;
358 	default:
359 		opts = "";
360 	}
361 
362 	mdb_printf("%#llr::nmadd %s -s %#llr %s\n",
363 	    sym->st_value, opts, sym->st_size, name);
364 
365 	return (0);
366 }
367 
368 /*ARGSUSED*/
369 static int
370 nm_cnt_any(void *data, const GElf_Sym *sym, const char *name,
371     const mdb_syminfo_t *sip, const char *obj)
372 {
373 	size_t *cntp = (size_t *)data;
374 	(*cntp)++;
375 	return (0);
376 }
377 
378 /*ARGSUSED*/
379 static int
380 nm_cnt_undef(void *data, const GElf_Sym *sym, const char *name,
381     const mdb_syminfo_t *sip, const char *obj)
382 {
383 	if (sym->st_shndx == SHN_UNDEF)
384 		return (nm_cnt_any(data, sym, name, sip, obj));
385 
386 	return (0);
387 }
388 
389 /*ARGSUSED*/
390 static int
391 nm_get_any(void *data, const GElf_Sym *sym, const char *name,
392     const mdb_syminfo_t *sip, const char *obj)
393 {
394 	nm_iter_info_t *niip = data;
395 	nm_sym_t **sympp = niip->nii_sympp;
396 
397 	(*sympp)->nm_sym = *sym;
398 	(*sympp)->nm_name = name;
399 	(*sympp)->nm_si = *sip;
400 	(*sympp)->nm_object = obj;
401 	(*sympp)->nm_fp = niip->nii_fp;
402 	(*sympp)++;
403 
404 	return (0);
405 }
406 
407 /*ARGSUSED*/
408 static int
409 nm_get_undef(void *data, const GElf_Sym *sym, const char *name,
410     const mdb_syminfo_t *sip, const char *obj)
411 {
412 	if (sym->st_shndx == SHN_UNDEF)
413 		return (nm_get_any(data, sym, name, sip, obj));
414 
415 	return (0);
416 }
417 
418 static int
419 nm_compare_name(const void *lp, const void *rp)
420 {
421 	const nm_sym_t *lhs = (nm_sym_t *)lp;
422 	const nm_sym_t *rhs = (nm_sym_t *)rp;
423 
424 	return (strcmp(lhs->nm_name, rhs->nm_name));
425 }
426 
427 static int
428 nm_compare_val(const void *lp, const void *rp)
429 {
430 	const nm_sym_t *lhs = (nm_sym_t *)lp;
431 	const nm_sym_t *rhs = (nm_sym_t *)rp;
432 
433 	return (lhs->nm_sym.st_value < rhs->nm_sym.st_value ? -1 :
434 	    (lhs->nm_sym.st_value > rhs->nm_sym.st_value ? 1 : 0));
435 }
436 
437 static int
438 nm_gelf_symtab_cb(void *data, const GElf_Sym *symp, const char *name, uint_t id)
439 {
440 	nm_gelf_symtab_t *ngsp = data;
441 
442 	ngsp->ngs_si.sym_id = id;
443 
444 	return (ngsp->ngs_cb(ngsp->ngs_arg, symp, name, &ngsp->ngs_si,
445 	    ngsp->ngs_object));
446 }
447 
448 static void
449 nm_gelf_symtab_iter(mdb_gelf_symtab_t *gst, const char *object, uint_t table,
450     mdb_tgt_sym_f *cb, void *arg)
451 {
452 	nm_gelf_symtab_t ngs;
453 
454 	ngs.ngs_cb = cb;
455 	ngs.ngs_arg = arg;
456 
457 	ngs.ngs_si.sym_table = table;
458 	ngs.ngs_object = object;
459 
460 	mdb_gelf_symtab_iter(gst, nm_gelf_symtab_cb, &ngs);
461 }
462 
463 static int nm_symbol_iter(const char *, uint_t, uint_t, mdb_tgt_sym_f *,
464     nm_iter_info_t *);
465 
466 /*ARGSUSED*/
467 static int
468 nm_object_iter_cb(void *data, const mdb_map_t *mp, const char *name)
469 {
470 	nm_object_iter_t *noip = data;
471 
472 	/*
473 	 * Since we're interating over all the objects in a target,
474 	 * don't return an error if we hit an object that we can't
475 	 * get symbol data for.
476 	 */
477 	if (nm_symbol_iter(name, noip->noi_which, noip->noi_type,
478 	    noip->noi_cb, noip->noi_niip) != 0)
479 		mdb_warn("unable to dump symbol data for: %s\n", name);
480 	return (0);
481 }
482 
483 int
484 nm_symbol_iter(const char *object, uint_t which, uint_t type,
485     mdb_tgt_sym_f *cb, nm_iter_info_t *niip)
486 {
487 	mdb_tgt_t *t = mdb.m_target;
488 
489 	if (object == MDB_TGT_OBJ_EVERY) {
490 		nm_object_iter_t noi;
491 
492 		noi.noi_which = which;
493 		noi.noi_type = type;
494 		noi.noi_cb = cb;
495 		noi.noi_niip = niip;
496 
497 		return (mdb_tgt_object_iter(t, nm_object_iter_cb, &noi));
498 	}
499 
500 	niip->nii_fp = mdb_tgt_name_to_ctf(t, object);
501 
502 	return (mdb_tgt_symbol_iter(t, object, which, type, cb, niip));
503 }
504 
505 /*ARGSUSED*/
506 int
507 cmd_nm(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
508 {
509 	enum {
510 		NM_DYNSYM	= 0x0001,	/* -D (use dynsym) */
511 		NM_DEC		= 0x0002,	/* -d (decimal output) */
512 		NM_GLOBAL	= 0x0004,	/* -g (globals only) */
513 		NM_NOHDRS	= 0x0008,	/* -h (suppress header) */
514 		NM_OCT		= 0x0010,	/* -o (octal output) */
515 		NM_UNDEF	= 0x0020,	/* -u (undefs only) */
516 		NM_HEX		= 0x0040,	/* -x (hex output) */
517 		NM_SORT_NAME	= 0x0080,	/* -n (sort by name) */
518 		NM_SORT_VALUE	= 0x0100,	/* -v (sort by value) */
519 		NM_PRVSYM	= 0x0200,	/* -P (use private symtab) */
520 		NM_PRTASGN	= 0x0400	/* -p (print in asgn syntax) */
521 	};
522 
523 	mdb_subopt_t opt_fmt_opts[] = {
524 		{ NM_FMT_INDEX, "ndx" },
525 		{ NM_FMT_VALUE, "val" },
526 		{ NM_FMT_SIZE, "sz" },
527 		{ NM_FMT_TYPE, "type" },
528 		{ NM_FMT_BIND, "bind" },
529 		{ NM_FMT_OTHER, "oth" },
530 		{ NM_FMT_SHNDX, "shndx" },
531 		{ NM_FMT_NAME, "name" },
532 		{ NM_FMT_CTYPE, "ctype" },
533 		{ NM_FMT_OBJECT, "obj" },
534 		{ NM_FMT_CTFID, "ctfid" },
535 		{ 0, NULL }
536 	};
537 
538 	mdb_subopt_t opt_type_opts[] = {
539 		{ NM_TYPE_NOTY, "noty" },
540 		{ NM_TYPE_OBJT, "objt" },
541 		{ NM_TYPE_FUNC, "func" },
542 		{ NM_TYPE_SECT, "sect" },
543 		{ NM_TYPE_FILE, "file" },
544 		{ NM_TYPE_COMM, "comm" },
545 		{ NM_TYPE_TLS, "tls" },
546 		{ NM_TYPE_REGI, "regi" },
547 		{ 0, NULL }
548 	};
549 
550 	uint_t optf = 0;
551 	uint_t opt_fmt;
552 	uint_t opt_types;
553 	int i;
554 
555 	mdb_tgt_sym_f *callback;
556 	uint_t which, type;
557 
558 	char *object = (char *)MDB_TGT_OBJ_EVERY;
559 	int hwidth;
560 	size_t nsyms = 0;
561 
562 	nm_sym_t *syms, *symp;
563 
564 	nm_iter_info_t nii;
565 
566 	/* default output columns */
567 	opt_fmt = NM_FMT_VALUE | NM_FMT_SIZE | NM_FMT_TYPE | NM_FMT_BIND |
568 	    NM_FMT_OTHER | NM_FMT_SHNDX | NM_FMT_NAME;
569 
570 	/* default output types */
571 	opt_types = NM_TYPE_NOTY | NM_TYPE_OBJT | NM_TYPE_FUNC | NM_TYPE_SECT |
572 	    NM_TYPE_FILE | NM_TYPE_COMM | NM_TYPE_TLS | NM_TYPE_REGI;
573 
574 	i = mdb_getopts(argc, argv,
575 	    'D', MDB_OPT_SETBITS, NM_DYNSYM, &optf,
576 	    'P', MDB_OPT_SETBITS, NM_PRVSYM, &optf,
577 	    'd', MDB_OPT_SETBITS, NM_DEC, &optf,
578 	    'g', MDB_OPT_SETBITS, NM_GLOBAL, &optf,
579 	    'h', MDB_OPT_SETBITS, NM_NOHDRS, &optf,
580 	    'n', MDB_OPT_SETBITS, NM_SORT_NAME, &optf,
581 	    'o', MDB_OPT_SETBITS, NM_OCT, &optf,
582 	    'p', MDB_OPT_SETBITS, NM_PRTASGN | NM_NOHDRS, &optf,
583 	    'u', MDB_OPT_SETBITS, NM_UNDEF, &optf,
584 	    'v', MDB_OPT_SETBITS, NM_SORT_VALUE, &optf,
585 	    'x', MDB_OPT_SETBITS, NM_HEX, &optf,
586 	    'f', MDB_OPT_SUBOPTS, opt_fmt_opts, &opt_fmt,
587 	    't', MDB_OPT_SUBOPTS, opt_type_opts, &opt_types,
588 	    NULL);
589 
590 	if (i != argc) {
591 		if (flags & DCMD_ADDRSPEC)
592 			return (DCMD_USAGE);
593 
594 		if (argc != 0 && (argc - i) == 1) {
595 			if (argv[i].a_type != MDB_TYPE_STRING ||
596 			    argv[i].a_un.a_str[0] == '-')
597 				return (DCMD_USAGE);
598 			else
599 				object = (char *)argv[i].a_un.a_str;
600 		} else
601 			return (DCMD_USAGE);
602 	}
603 
604 	if ((optf & (NM_DEC | NM_HEX | NM_OCT)) == 0) {
605 		switch (mdb.m_radix) {
606 		case 8:
607 			optf |= NM_OCT;
608 			break;
609 		case 10:
610 			optf |= NM_DEC;
611 			break;
612 		default:
613 			optf |= NM_HEX;
614 		}
615 	}
616 
617 	switch (optf & (NM_DEC | NM_HEX | NM_OCT)) {
618 	case NM_DEC:
619 #ifdef _LP64
620 		nii.nii_pfmt = "%-20llu";
621 		nii.nii_ofmt = "%-5u";
622 		hwidth = 20;
623 #else
624 		nii.nii_pfmt = "%-10llu";
625 		nii.nii_ofmt = "%-5u";
626 		hwidth = 10;
627 #endif
628 		break;
629 	case NM_HEX:
630 #ifdef _LP64
631 		nii.nii_pfmt = "0x%016llx";
632 		nii.nii_ofmt = "0x%-3x";
633 		hwidth = 18;
634 #else
635 		nii.nii_pfmt = "0x%08llx";
636 		nii.nii_ofmt = "0x%-3x";
637 		hwidth = 10;
638 #endif
639 		break;
640 	case NM_OCT:
641 #ifdef _LP64
642 		nii.nii_pfmt = "%-22llo";
643 		nii.nii_ofmt = "%-5o";
644 		hwidth = 22;
645 #else
646 		nii.nii_pfmt = "%-11llo";
647 		nii.nii_ofmt = "%-5o";
648 		hwidth = 11;
649 #endif
650 		break;
651 	default:
652 		mdb_warn("-d/-o/-x options are mutually exclusive\n");
653 		return (DCMD_USAGE);
654 	}
655 
656 	if (object != MDB_TGT_OBJ_EVERY && (optf & NM_PRVSYM)) {
657 		mdb_warn("-P/object options are mutually exclusive\n");
658 		return (DCMD_USAGE);
659 	}
660 
661 	if ((flags & DCMD_ADDRSPEC) && (optf & NM_PRVSYM)) {
662 		mdb_warn("-P/address options are mutually exclusive\n");
663 		return (DCMD_USAGE);
664 	}
665 
666 	if (!(optf & NM_NOHDRS)) {
667 		mdb_printf("%<u>");
668 		mdb_table_print(opt_fmt, " ",
669 		    MDB_TBL_PRNT, NM_FMT_INDEX, "Index",
670 		    MDB_TBL_PRNT, NM_FMT_OBJECT, "%-15s", "Object",
671 		    MDB_TBL_PRNT, NM_FMT_VALUE, "%-*s", hwidth, "Value",
672 		    MDB_TBL_PRNT, NM_FMT_SIZE, "%-*s", hwidth, "Size",
673 		    MDB_TBL_PRNT, NM_FMT_TYPE, "%-5s", "Type",
674 		    MDB_TBL_PRNT, NM_FMT_BIND, "%-5s", "Bind",
675 		    MDB_TBL_PRNT, NM_FMT_OTHER, "%-5s", "Other",
676 		    MDB_TBL_PRNT, NM_FMT_SHNDX, "%-8s", "Shndx",
677 		    MDB_TBL_PRNT, NM_FMT_CTFID, "%-9s", "CTF ID",
678 		    MDB_TBL_PRNT, NM_FMT_CTYPE, "%-50s", "C Type",
679 		    MDB_TBL_PRNT, NM_FMT_NAME, "%s", "Name",
680 		    MDB_TBL_DONE);
681 
682 		mdb_printf("%</u>\n");
683 	}
684 
685 	nii.nii_flags = opt_fmt;
686 	nii.nii_types = opt_types;
687 
688 	if (optf & NM_DYNSYM)
689 		which = MDB_TGT_DYNSYM;
690 	else
691 		which = MDB_TGT_SYMTAB;
692 
693 	if (optf & NM_GLOBAL)
694 		type = MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_ANY;
695 	else
696 		type = MDB_TGT_BIND_ANY | MDB_TGT_TYPE_ANY;
697 
698 	if (flags & DCMD_ADDRSPEC)
699 		optf |= NM_SORT_NAME; /* use sorting path if only one symbol */
700 
701 	if (optf & (NM_SORT_NAME | NM_SORT_VALUE)) {
702 		char name[MDB_SYM_NAMLEN];
703 		GElf_Sym sym;
704 		mdb_syminfo_t si;
705 
706 		if (optf & NM_UNDEF)
707 			callback = nm_cnt_undef;
708 		else
709 			callback = nm_cnt_any;
710 
711 		if (flags & DCMD_ADDRSPEC) {
712 			const mdb_map_t *mp;
713 			/* gather relevant data for the specified addr */
714 
715 			nii.nii_fp = mdb_tgt_addr_to_ctf(mdb.m_target, addr);
716 
717 			if (mdb_tgt_lookup_by_addr(mdb.m_target, addr,
718 			    MDB_SYM_FUZZY, name, sizeof (name), &sym,
719 			    &si) == -1) {
720 				mdb_warn("%lr", addr);
721 				return (DCMD_ERR);
722 			}
723 
724 			if ((mp = mdb_tgt_addr_to_map(mdb.m_target, addr))
725 			    != NULL) {
726 				object = mdb_alloc(strlen(mp->map_name) + 1,
727 				    UM_SLEEP | UM_GC);
728 
729 				(void) strcpy(object, mp->map_name);
730 
731 				/*
732 				 * Try to find a better match for the syminfo.
733 				 */
734 				(void) mdb_tgt_lookup_by_name(mdb.m_target,
735 				    object, name, &sym, &si);
736 			}
737 
738 			(void) callback(&nsyms, &sym, name, &si, object);
739 
740 		} else if (optf & NM_PRVSYM) {
741 			nsyms = mdb_gelf_symtab_size(mdb.m_prsym);
742 		} else {
743 			(void) mdb_tgt_symbol_iter(mdb.m_target, object,
744 			    which, type, callback, &nsyms);
745 		}
746 
747 		if (nsyms == 0)
748 			return (DCMD_OK);
749 
750 		syms = symp = mdb_alloc(sizeof (nm_sym_t) * nsyms,
751 		    UM_SLEEP | UM_GC);
752 
753 		nii.nii_sympp = &symp;
754 
755 		if (optf & NM_UNDEF)
756 			callback = nm_get_undef;
757 		else
758 			callback = nm_get_any;
759 
760 		if (flags & DCMD_ADDRSPEC) {
761 			(void) callback(&nii, &sym, name, &si, object);
762 		} else if (optf & NM_PRVSYM) {
763 			nm_gelf_symtab_iter(mdb.m_prsym, object, MDB_TGT_PRVSYM,
764 			    callback, &nii);
765 		} else if (nm_symbol_iter(object, which, type, callback,
766 		    &nii) == -1) {
767 			mdb_warn("failed to iterate over symbols");
768 			return (DCMD_ERR);
769 		}
770 
771 		if (optf & NM_SORT_NAME)
772 			qsort(syms, nsyms, sizeof (nm_sym_t), nm_compare_name);
773 		else
774 			qsort(syms, nsyms, sizeof (nm_sym_t), nm_compare_val);
775 	}
776 
777 	if ((optf & (NM_PRVSYM | NM_PRTASGN)) == (NM_PRVSYM | NM_PRTASGN))
778 		callback = nm_asgn;
779 	else if (optf & NM_UNDEF)
780 		callback = nm_undef;
781 	else
782 		callback = nm_any;
783 
784 	if (optf & (NM_SORT_NAME | NM_SORT_VALUE)) {
785 		for (symp = syms; nsyms-- != 0; symp++) {
786 			nii.nii_fp = symp->nm_fp;
787 
788 			callback(&nii, &symp->nm_sym, symp->nm_name,
789 			    &symp->nm_si, symp->nm_object);
790 		}
791 
792 	} else {
793 		if (optf & NM_PRVSYM) {
794 			nm_gelf_symtab_iter(mdb.m_prsym, object, MDB_TGT_PRVSYM,
795 			    callback, &nii);
796 
797 		} else if (nm_symbol_iter(object, which, type, callback, &nii)
798 		    == -1) {
799 			mdb_warn("failed to iterate over symbols");
800 			return (DCMD_ERR);
801 		}
802 	}
803 
804 	return (DCMD_OK);
805 }
806 
807 int
808 cmd_nmadd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
809 {
810 	uintptr_t opt_e = 0, opt_s = 0;
811 	uint_t opt_f = FALSE, opt_o = FALSE;
812 
813 	GElf_Sym sym;
814 	int i;
815 
816 	if (!(flags & DCMD_ADDRSPEC))
817 		return (DCMD_USAGE);
818 
819 	i = mdb_getopts(argc, argv,
820 	    'f', MDB_OPT_SETBITS, TRUE, &opt_f,
821 	    'o', MDB_OPT_SETBITS, TRUE, &opt_o,
822 	    'e', MDB_OPT_UINTPTR, &opt_e,
823 	    's', MDB_OPT_UINTPTR, &opt_s, NULL);
824 
825 	if (i != (argc - 1) || argv[i].a_type != MDB_TYPE_STRING ||
826 	    argv[i].a_un.a_str[0] == '-' || argv[i].a_un.a_str[0] == '+')
827 		return (DCMD_USAGE);
828 
829 	if (opt_e && opt_e < addr) {
830 		mdb_warn("end (%p) is less than start address (%p)\n",
831 		    (void *)opt_e, (void *)addr);
832 		return (DCMD_USAGE);
833 	}
834 
835 	if (mdb_gelf_symtab_lookup_by_name(mdb.m_prsym,
836 	    argv[i].a_un.a_str, &sym, NULL) == -1) {
837 		bzero(&sym, sizeof (sym));
838 		sym.st_info = GELF_ST_INFO(STB_GLOBAL, STT_NOTYPE);
839 	}
840 
841 	if (opt_f)
842 		sym.st_info = GELF_ST_INFO(STB_GLOBAL, STT_FUNC);
843 	if (opt_o)
844 		sym.st_info = GELF_ST_INFO(STB_GLOBAL, STT_OBJECT);
845 	if (opt_e)
846 		sym.st_size = (GElf_Xword)(opt_e - addr);
847 	if (opt_s)
848 		sym.st_size = (GElf_Xword)(opt_s);
849 	sym.st_value = (GElf_Addr)addr;
850 
851 	mdb_gelf_symtab_insert(mdb.m_prsym, argv[i].a_un.a_str, &sym);
852 
853 	mdb_iob_printf(mdb.m_out, "added %s, value=%llr size=%llr\n",
854 	    argv[i].a_un.a_str, sym.st_value, sym.st_size);
855 
856 	return (DCMD_OK);
857 }
858 
859 /*ARGSUSED*/
860 int
861 cmd_nmdel(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
862 {
863 	const char *name;
864 	GElf_Sym sym;
865 	uint_t id;
866 
867 	if (argc != 1 || argv->a_type != MDB_TYPE_STRING ||
868 	    argv->a_un.a_str[0] == '-' || (flags & DCMD_ADDRSPEC))
869 		return (DCMD_USAGE);
870 
871 	name = argv->a_un.a_str;
872 
873 	if (mdb_gelf_symtab_lookup_by_name(mdb.m_prsym, name, &sym, &id) == 0) {
874 		mdb_gelf_symtab_delete(mdb.m_prsym, name, &sym);
875 		mdb_printf("deleted %s, value=%llr size=%llr\n",
876 		    name, sym.st_value, sym.st_size);
877 		return (DCMD_OK);
878 	}
879 
880 	mdb_warn("symbol '%s' not found in private symbol table\n", name);
881 	return (DCMD_ERR);
882 }
883 
884 void
885 nm_help(void)
886 {
887 	mdb_printf("-D         print .dynsym instead of .symtab\n"
888 	    "-P         print private symbol table instead of .symtab\n"
889 	    "-d         print value and size in decimal\n"
890 	    "-g         only print global symbols\n"
891 	    "-h         suppress header line\n"
892 	    "-n         sort symbols by name\n"
893 	    "-o         print value and size in octal\n"
894 	    "-p         print symbols as a series of ::nmadd commands\n"
895 	    "-u         only print undefined symbols\n"
896 	    "-v         sort symbols by value\n"
897 	    "-x         print value and size in hexadecimal\n"
898 	    "-f format  use specified format\n"
899 	    "           ndx, val, sz, type, bind, oth, shndx, "
900 	    "name, ctype, obj\n"
901 	    "-t types   display symbols with the specified types\n"
902 	    "           noty, objt, func, sect, file, regi\n"
903 	    "obj        specify object whose symbol table should be used\n");
904 }
905 
906 void
907 nmadd_help(void)
908 {
909 	mdb_printf("-f       set type of symbol to STT_FUNC\n"
910 	    "-o       set type of symbol to STT_OBJECT\n"
911 	    "-e end   set size of symbol to end - start address\n"
912 	    "-s size  set size of symbol to explicit value\n"
913 	    "name     specify symbol name to add\n");
914 }
915