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