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 2007 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  *
  26  * Copyright 2011 Jason King.  All rights reserved.
  27  * Copyright 2012 Joshua M. Clulow <josh@sysmgr.org>
  28  * Copyright 2015 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
  29  * Copyright 2018, Joyent, Inc.
  30  */
  31 
  32 #include <ctype.h>
  33 #include <getopt.h>
  34 #include <stdio.h>
  35 #include <stdlib.h>
  36 #include <string.h>
  37 #include <sys/sysmacros.h>
  38 #include <sys/elf_SPARC.h>
  39 
  40 #include <libdisasm.h>
  41 
  42 #include "dis_target.h"
  43 #include "dis_util.h"
  44 #include "dis_list.h"
  45 
  46 int g_demangle;         /* Demangle C++ names */
  47 int g_quiet;            /* Quiet mode */
  48 int g_numeric;          /* Numeric mode */
  49 int g_flags;            /* libdisasm language flags */
  50 int g_doall;            /* true if no functions or sections were given */
  51 
  52 dis_namelist_t *g_funclist;     /* list of functions to disassemble, if any */
  53 dis_namelist_t *g_seclist;      /* list of sections to disassemble, if any */
  54 
  55 /*
  56  * Section options for -d, -D, and -s
  57  */
  58 #define DIS_DATA_RELATIVE       1
  59 #define DIS_DATA_ABSOLUTE       2
  60 #define DIS_TEXT                3
  61 
  62 /*
  63  * libdisasm callback data.  Keeps track of current data (function or section)
  64  * and offset within that data.
  65  */
  66 typedef struct dis_buffer {
  67         dis_tgt_t       *db_tgt;        /* current dis target */
  68         void            *db_data;       /* function or section data */
  69         uint64_t        db_addr;        /* address of function start */
  70         size_t          db_size;        /* size of data */
  71         uint64_t        db_nextaddr;    /* next address to be read */
  72 } dis_buffer_t;
  73 
  74 #define MINSYMWIDTH     22      /* Minimum width of symbol portion of line */
  75 
  76 /*
  77  * Given a symbol+offset as returned by dis_tgt_lookup(), print an appropriately
  78  * formatted symbol, based on the offset and current setttings.
  79  */
  80 void
  81 getsymname(uint64_t addr, const char *symbol, off_t offset, char *buf,
  82     size_t buflen)
  83 {
  84         if (symbol == NULL || g_numeric) {
  85                 if (g_flags & DIS_OCTAL)
  86                         (void) snprintf(buf, buflen, "0%llo", addr);
  87                 else
  88                         (void) snprintf(buf, buflen, "0x%llx", addr);
  89         } else {
  90                 if (g_demangle)
  91                         symbol = dis_demangle(symbol);
  92 
  93                 if (offset == 0)
  94                         (void) snprintf(buf, buflen, "%s", symbol);
  95                 else if (g_flags & DIS_OCTAL)
  96                         (void) snprintf(buf, buflen, "%s+0%o", symbol, offset);
  97                 else
  98                         (void) snprintf(buf, buflen, "%s+0x%x", symbol, offset);
  99         }
 100 }
 101 
 102 /*
 103  * Determine if we are on an architecture with fixed-size instructions,
 104  * and if so, what size they are.
 105  */
 106 static int
 107 insn_size(dis_handle_t *dhp)
 108 {
 109         int min = dis_min_instrlen(dhp);
 110         int max = dis_max_instrlen(dhp);
 111 
 112         if (min == max)
 113                 return (min);
 114 
 115         return (0);
 116 }
 117 
 118 /*
 119  * The main disassembly routine.  Given a fixed-sized buffer and starting
 120  * address, disassemble the data using the supplied target and libdisasm handle.
 121  */
 122 void
 123 dis_data(dis_tgt_t *tgt, dis_handle_t *dhp, uint64_t addr, void *data,
 124     size_t datalen)
 125 {
 126         dis_buffer_t db = { 0 };
 127         char buf[BUFSIZE];
 128         char symbuf[BUFSIZE];
 129         const char *symbol;
 130         const char *last_symbol;
 131         off_t symoffset;
 132         int i;
 133         int bytesperline;
 134         size_t symsize;
 135         int isfunc;
 136         size_t symwidth = 0;
 137         int ret;
 138         int insz = insn_size(dhp);
 139 
 140         db.db_tgt = tgt;
 141         db.db_data = data;
 142         db.db_addr = addr;
 143         db.db_size = datalen;
 144 
 145         dis_set_data(dhp, &db);
 146 
 147         if ((bytesperline = dis_max_instrlen(dhp)) > 6)
 148                 bytesperline = 6;
 149 
 150         symbol = NULL;
 151 
 152         while (addr < db.db_addr + db.db_size) {
 153 
 154                 ret = dis_disassemble(dhp, addr, buf, BUFSIZE);
 155                 if (ret != 0 && insz > 0) {
 156                         /*
 157                          * Since we know instructions are fixed size, we
 158                          * always know the address of the next instruction
 159                          */
 160                         (void) snprintf(buf, sizeof (buf),
 161                             "*** invalid opcode ***");
 162                         db.db_nextaddr = addr + insz;
 163 
 164                 } else if (ret != 0) {
 165                         off_t next;
 166 
 167                         (void) snprintf(buf, sizeof (buf),
 168                             "*** invalid opcode ***");
 169 
 170                         /*
 171                          * On architectures with variable sized instructions
 172                          * we have no way to figure out where the next
 173                          * instruction starts if we encounter an invalid
 174                          * instruction.  Instead we print the rest of the
 175                          * instruction stream as hex until we reach the
 176                          * next valid symbol in the section.
 177                          */
 178                         if ((next = dis_tgt_next_symbol(tgt, addr)) == 0) {
 179                                 db.db_nextaddr = db.db_addr + db.db_size;
 180                         } else {
 181                                 if (next > db.db_size)
 182                                         db.db_nextaddr = db.db_addr +
 183                                             db.db_size;
 184                                 else
 185                                         db.db_nextaddr = addr + next;
 186                         }
 187                 }
 188 
 189                 /*
 190                  * Print out the line as:
 191                  *
 192                  *      address:        bytes   text
 193                  *
 194                  * If there are more than 6 bytes in any given instruction,
 195                  * spread the bytes across two lines.  We try to get symbolic
 196                  * information for the address, but if that fails we print out
 197                  * the numeric address instead.
 198                  *
 199                  * We try to keep the address portion of the text aligned at
 200                  * MINSYMWIDTH characters.  If we are disassembling a function
 201                  * with a long name, this can be annoying.  So we pick a width
 202                  * based on the maximum width that the current symbol can be.
 203                  * This at least produces text aligned within each function.
 204                  */
 205                 last_symbol = symbol;
 206                 symbol = dis_tgt_lookup(tgt, addr, &symoffset, 1, &symsize,
 207                     &isfunc);
 208                 if (symbol == NULL) {
 209                         symbol = dis_find_section(tgt, addr, &symoffset);
 210                         symsize = symoffset;
 211                 }
 212 
 213                 if (symbol != last_symbol)
 214                         getsymname(addr, symbol, symsize, symbuf,
 215                             sizeof (symbuf));
 216 
 217                 symwidth = MAX(symwidth, strlen(symbuf));
 218                 getsymname(addr, symbol, symoffset, symbuf, sizeof (symbuf));
 219 
 220                 /*
 221                  * If we've crossed a new function boundary, print out the
 222                  * function name on a blank line.
 223                  */
 224                 if (!g_quiet && symoffset == 0 && symbol != NULL && isfunc)
 225                         (void) printf("%s()\n", symbol);
 226 
 227                 (void) printf("    %s:%*s ", symbuf,
 228                     symwidth - strlen(symbuf), "");
 229 
 230                 /* print bytes */
 231                 for (i = 0; i < MIN(bytesperline, (db.db_nextaddr - addr));
 232                     i++) {
 233                         int byte = *((uchar_t *)data + (addr - db.db_addr) + i);
 234                         if (g_flags & DIS_OCTAL)
 235                                 (void) printf("%03o ", byte);
 236                         else
 237                                 (void) printf("%02x ", byte);
 238                 }
 239 
 240                 /* trailing spaces for missing bytes */
 241                 for (; i < bytesperline; i++) {
 242                         if (g_flags & DIS_OCTAL)
 243                                 (void) printf("    ");
 244                         else
 245                                 (void) printf("   ");
 246                 }
 247 
 248                 /* contents of disassembly */
 249                 (void) printf(" %s", buf);
 250 
 251                 /* excess bytes that spill over onto subsequent lines */
 252                 for (; i < db.db_nextaddr - addr; i++) {
 253                         int byte = *((uchar_t *)data + (addr - db.db_addr) + i);
 254                         if (i % bytesperline == 0)
 255                                 (void) printf("\n    %*s  ", symwidth, "");
 256                         if (g_flags & DIS_OCTAL)
 257                                 (void) printf("%03o ", byte);
 258                         else
 259                                 (void) printf("%02x ", byte);
 260                 }
 261 
 262                 (void) printf("\n");
 263 
 264                 addr = db.db_nextaddr;
 265         }
 266 }
 267 
 268 /*
 269  * libdisasm wrapper around symbol lookup.  Invoke the target-specific lookup
 270  * function, and convert the result using getsymname().
 271  */
 272 int
 273 do_lookup(void *data, uint64_t addr, char *buf, size_t buflen, uint64_t *start,
 274     size_t *symlen)
 275 {
 276         dis_buffer_t *db = data;
 277         const char *symbol;
 278         off_t offset;
 279         size_t size;
 280 
 281         /*
 282          * If NULL symbol is returned, getsymname takes care of
 283          * printing appropriate address in buf instead of symbol.
 284          */
 285         symbol = dis_tgt_lookup(db->db_tgt, addr, &offset, 0, &size, NULL);
 286 
 287         if (buf != NULL)
 288                 getsymname(addr, symbol, offset, buf, buflen);
 289 
 290         if (start != NULL)
 291                 *start = addr - offset;
 292         if (symlen != NULL)
 293                 *symlen = size;
 294 
 295         if (symbol == NULL)
 296                 return (-1);
 297 
 298         return (0);
 299 }
 300 
 301 /*
 302  * libdisasm wrapper around target reading.  libdisasm will always read data
 303  * in order, so update our current offset within the buffer appropriately.
 304  * We only support reading from within the current object; libdisasm should
 305  * never ask us to do otherwise.
 306  */
 307 int
 308 do_read(void *data, uint64_t addr, void *buf, size_t len)
 309 {
 310         dis_buffer_t *db = data;
 311         size_t offset;
 312 
 313         if (addr < db->db_addr || addr >= db->db_addr + db->db_size)
 314                 return (-1);
 315 
 316         offset = addr - db->db_addr;
 317         len = MIN(len, db->db_size - offset);
 318 
 319         (void) memcpy(buf, (char *)db->db_data + offset, len);
 320 
 321         db->db_nextaddr = addr + len;
 322 
 323         return (len);
 324 }
 325 
 326 /*
 327  * Routine to dump raw data in a human-readable format.  Used by the -d and -D
 328  * options.  We model our output after the xxd(1) program, which gives nicely
 329  * formatted output, along with an ASCII translation of the result.
 330  */
 331 void
 332 dump_data(uint64_t addr, void *data, size_t datalen)
 333 {
 334         uintptr_t curaddr = addr & (~0xf);
 335         uint8_t *bytes = data;
 336         int i;
 337         int width;
 338 
 339         /*
 340          * Determine if the address given to us fits in 32-bit range, in which
 341          * case use a 4-byte width.
 342          */
 343         if (((addr + datalen) & 0xffffffff00000000ULL) == 0ULL)
 344                 width = 8;
 345         else
 346                 width = 16;
 347 
 348         while (curaddr < addr + datalen) {
 349                 /*
 350                  * Display leading address
 351                  */
 352                 (void) printf("%0*x: ", width, curaddr);
 353 
 354                 /*
 355                  * Print out data in two-byte chunks.  If the current address
 356                  * is before the starting address or after the end of the
 357                  * section, print spaces.
 358                  */
 359                 for (i = 0; i < 16; i++) {
 360                         if (curaddr + i < addr ||curaddr + i >= addr + datalen)
 361                                 (void) printf("  ");
 362                         else
 363                                 (void) printf("%02x",
 364                                     bytes[curaddr + i - addr]);
 365 
 366                         if (i & 1)
 367                                 (void) printf(" ");
 368                 }
 369 
 370                 (void) printf(" ");
 371 
 372                 /*
 373                  * Print out the ASCII representation
 374                  */
 375                 for (i = 0; i < 16; i++) {
 376                         if (curaddr + i < addr ||
 377                             curaddr + i >= addr + datalen) {
 378                                 (void) printf(" ");
 379                         } else {
 380                                 uint8_t byte = bytes[curaddr + i - addr];
 381                                 if (isprint(byte))
 382                                         (void) printf("%c", byte);
 383                                 else
 384                                         (void) printf(".");
 385                         }
 386                 }
 387 
 388                 (void) printf("\n");
 389 
 390                 curaddr += 16;
 391         }
 392 }
 393 
 394 /*
 395  * Disassemble a section implicitly specified as part of a file.  This function
 396  * is called for all sections when no other flags are specified.  We ignore any
 397  * data sections, and print out only those sections containing text.
 398  */
 399 void
 400 dis_text_section(dis_tgt_t *tgt, dis_scn_t *scn, void *data)
 401 {
 402         dis_handle_t *dhp = data;
 403 
 404         /* ignore data sections */
 405         if (!dis_section_istext(scn))
 406                 return;
 407 
 408         if (!g_quiet)
 409                 (void) printf("\nsection %s\n", dis_section_name(scn));
 410 
 411         dis_data(tgt, dhp, dis_section_addr(scn), dis_section_data(scn),
 412             dis_section_size(scn));
 413 }
 414 
 415 /*
 416  * Structure passed to dis_named_{section,function} which keeps track of both
 417  * the target and the libdisasm handle.
 418  */
 419 typedef struct callback_arg {
 420         dis_tgt_t       *ca_tgt;
 421         dis_handle_t    *ca_handle;
 422 } callback_arg_t;
 423 
 424 /*
 425  * Disassemble a section explicitly named with -s, -d, or -D.  The 'type'
 426  * argument contains the type of argument given.  Pass the data onto the
 427  * appropriate helper routine.
 428  */
 429 void
 430 dis_named_section(dis_scn_t *scn, int type, void *data)
 431 {
 432         callback_arg_t *ca = data;
 433 
 434         if (!g_quiet)
 435                 (void) printf("\nsection %s\n", dis_section_name(scn));
 436 
 437         switch (type) {
 438         case DIS_DATA_RELATIVE:
 439                 dump_data(0, dis_section_data(scn), dis_section_size(scn));
 440                 break;
 441         case DIS_DATA_ABSOLUTE:
 442                 dump_data(dis_section_addr(scn), dis_section_data(scn),
 443                     dis_section_size(scn));
 444                 break;
 445         case DIS_TEXT:
 446                 dis_data(ca->ca_tgt, ca->ca_handle, dis_section_addr(scn),
 447                     dis_section_data(scn), dis_section_size(scn));
 448                 break;
 449         }
 450 }
 451 
 452 /*
 453  * Disassemble a function explicitly specified with '-F'.  The 'type' argument
 454  * is unused.
 455  */
 456 /* ARGSUSED */
 457 void
 458 dis_named_function(dis_func_t *func, int type, void *data)
 459 {
 460         callback_arg_t *ca = data;
 461 
 462         dis_data(ca->ca_tgt, ca->ca_handle, dis_function_addr(func),
 463             dis_function_data(func), dis_function_size(func));
 464 }
 465 
 466 /*
 467  * Disassemble a complete file.  First, we determine the type of the file based
 468  * on the ELF machine type, and instantiate a version of the disassembler
 469  * appropriate for the file.  We then resolve any named sections or functions
 470  * against the file, and iterate over the results (or all sections if no flags
 471  * were specified).
 472  */
 473 void
 474 dis_file(const char *filename)
 475 {
 476         dis_tgt_t *tgt, *current;
 477         dis_scnlist_t *sections;
 478         dis_funclist_t *functions;
 479         dis_handle_t *dhp;
 480         GElf_Ehdr ehdr;
 481 
 482         /*
 483          * First, initialize the target
 484          */
 485         if ((tgt = dis_tgt_create(filename)) == NULL)
 486                 return;
 487 
 488         if (!g_quiet)
 489                 (void) printf("disassembly for %s\n\n",  filename);
 490 
 491         /*
 492          * A given file may contain multiple targets (if it is an archive, for
 493          * example).  We iterate over all possible targets if this is the case.
 494          */
 495         for (current = tgt; current != NULL; current = dis_tgt_next(current)) {
 496                 dis_tgt_ehdr(current, &ehdr);
 497 
 498                 /*
 499                  * Eventually, this should probably live within libdisasm, and
 500                  * we should be able to disassemble targets from different
 501                  * architectures.  For now, we only support objects as the
 502                  * native machine type.
 503                  */
 504                 switch (ehdr.e_machine) {
 505                 case EM_SPARC:
 506                         if (ehdr.e_ident[EI_CLASS] != ELFCLASS32 ||
 507                             ehdr.e_ident[EI_DATA] != ELFDATA2MSB) {
 508                                 warn("invalid E_IDENT field for SPARC object");
 509                                 return;
 510                         }
 511                         g_flags |= DIS_SPARC_V8;
 512                         break;
 513 
 514                 case EM_SPARC32PLUS:
 515                 {
 516                         uint64_t flags = ehdr.e_flags & EF_SPARC_32PLUS_MASK;
 517 
 518                         if (ehdr.e_ident[EI_CLASS] != ELFCLASS32 ||
 519                             ehdr.e_ident[EI_DATA] != ELFDATA2MSB) {
 520                                 warn("invalid E_IDENT field for SPARC object");
 521                                 return;
 522                         }
 523 
 524                         if (flags != 0 &&
 525                             (flags & (EF_SPARC_32PLUS | EF_SPARC_SUN_US1 |
 526                             EF_SPARC_SUN_US3)) != EF_SPARC_32PLUS)
 527                                 g_flags |= DIS_SPARC_V9 | DIS_SPARC_V9_SGI;
 528                         else
 529                                 g_flags |= DIS_SPARC_V9;
 530                         break;
 531                 }
 532 
 533                 case EM_SPARCV9:
 534                         if (ehdr.e_ident[EI_CLASS] != ELFCLASS64 ||
 535                             ehdr.e_ident[EI_DATA] != ELFDATA2MSB) {
 536                                 warn("invalid E_IDENT field for SPARC object");
 537                                 return;
 538                         }
 539 
 540                         g_flags |= DIS_SPARC_V9 | DIS_SPARC_V9_SGI;
 541                         break;
 542 
 543                 case EM_386:
 544                         g_flags |= DIS_X86_SIZE32;
 545                         break;
 546 
 547                 case EM_AMD64:
 548                         g_flags |= DIS_X86_SIZE64;
 549                         break;
 550 
 551                 case EM_S370:
 552                         g_flags |= DIS_S370;
 553 
 554                         if (ehdr.e_ident[EI_CLASS] != ELFCLASS32 ||
 555                             ehdr.e_ident[EI_DATA] != ELFDATA2MSB) {
 556                                 warn("invalid E_IDENT field for S370 object");
 557                                 return;
 558                         }
 559                         break;
 560 
 561                 case EM_S390:
 562                         /*
 563                          * Both 390 and z/Architecture use EM_S390, the only
 564                          * differences is the class: ELFCLASS32 for plain
 565                          * old s390 and ELFCLASS64 for z/Architecture (aka.
 566                          * s390x).
 567                          */
 568                         if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
 569                                 g_flags |= DIS_S390_31;
 570                         } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
 571                                 g_flags |= DIS_S390_64;
 572                         } else {
 573                                 warn("invalid E_IDENT field for S390 object");
 574                                 return;
 575                         }
 576 
 577                         if (ehdr.e_ident[EI_DATA] != ELFDATA2MSB) {
 578                                 warn("invalid E_IDENT field for S390 object");
 579                                 return;
 580                         }
 581                         break;
 582 
 583                 case EM_RISCV:
 584                         /*
 585                          * RISC-V is defined to be litle endian. The current ISA
 586                          * makes it clear that the 64-bit instructions can
 587                          * co-eixst with the 32-bit ones and therefore we don't
 588                          * need a separate elf class at this time.
 589                          */
 590                         if (ehdr.e_ident[EI_DATA] != ELFDATA2LSB) {
 591                                 warn("invalid EI_DATA field for RISC-V object");
 592                                 return;
 593                         }
 594 
 595                         if (ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
 596                                 g_flags |= DIS_RISCV_32;
 597                         } else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64) {
 598                                 g_flags |= DIS_RISCV_64;
 599                         } else {
 600                                 warn("invalid EI_CLASS field for RISC-V "
 601                                     "object");
 602                                 return;
 603                         }
 604                         break;
 605 
 606                 default:
 607                         die("%s: unsupported ELF machine 0x%x", filename,
 608                             ehdr.e_machine);
 609                 }
 610 
 611                 /*
 612                  * If ET_REL (.o), printing immediate symbols is likely to
 613                  * result in garbage, as symbol lookups on unrelocated
 614                  * immediates find false and useless matches.
 615                  */
 616 
 617                 if (ehdr.e_type == ET_REL)
 618                         g_flags |= DIS_NOIMMSYM;
 619 
 620                 if (!g_quiet && dis_tgt_member(current) != NULL)
 621                         (void) printf("\narchive member %s\n",
 622                             dis_tgt_member(current));
 623 
 624                 /*
 625                  * Instantiate a libdisasm handle based on the file type.
 626                  */
 627                 if ((dhp = dis_handle_create(g_flags, current, do_lookup,
 628                     do_read)) == NULL)
 629                         die("%s: failed to initialize disassembler: %s",
 630                             filename, dis_strerror(dis_errno()));
 631 
 632                 if (g_doall) {
 633                         /*
 634                          * With no arguments, iterate over all sections and
 635                          * disassemble only those that contain text.
 636                          */
 637                         dis_tgt_section_iter(current, dis_text_section, dhp);
 638                 } else {
 639                         callback_arg_t ca;
 640 
 641                         ca.ca_tgt = current;
 642                         ca.ca_handle = dhp;
 643 
 644                         /*
 645                          * If sections or functions were explicitly specified,
 646                          * resolve those names against the object, and iterate
 647                          * over just the resulting data.
 648                          */
 649                         sections = dis_namelist_resolve_sections(g_seclist,
 650                             current);
 651                         functions = dis_namelist_resolve_functions(g_funclist,
 652                             current);
 653 
 654                         dis_scnlist_iter(sections, dis_named_section, &ca);
 655                         dis_funclist_iter(functions, dis_named_function, &ca);
 656 
 657                         dis_scnlist_destroy(sections);
 658                         dis_funclist_destroy(functions);
 659                 }
 660 
 661                 dis_handle_destroy(dhp);
 662         }
 663 
 664         dis_tgt_destroy(tgt);
 665 }
 666 
 667 void
 668 usage(void)
 669 {
 670         (void) fprintf(stderr, "usage: dis [-CVoqn] [-d sec] \n");
 671         (void) fprintf(stderr, "\t[-D sec] [-F function] [-t sec] file ..\n");
 672         exit(2);
 673 }
 674 
 675 typedef struct lib_node {
 676         char *path;
 677         struct lib_node *next;
 678 } lib_node_t;
 679 
 680 int
 681 main(int argc, char **argv)
 682 {
 683         int optchar;
 684         int i;
 685         lib_node_t *libs = NULL;
 686 
 687         g_funclist = dis_namelist_create();
 688         g_seclist = dis_namelist_create();
 689 
 690         while ((optchar = getopt(argc, argv, "Cd:D:F:l:Lot:Vqn")) != -1) {
 691                 switch (optchar) {
 692                 case 'C':
 693                         g_demangle = 1;
 694                         break;
 695                 case 'd':
 696                         dis_namelist_add(g_seclist, optarg, DIS_DATA_RELATIVE);
 697                         break;
 698                 case 'D':
 699                         dis_namelist_add(g_seclist, optarg, DIS_DATA_ABSOLUTE);
 700                         break;
 701                 case 'F':
 702                         dis_namelist_add(g_funclist, optarg, 0);
 703                         break;
 704                 case 'l': {
 705                         /*
 706                          * The '-l foo' option historically would attempt to
 707                          * disassemble '$LIBDIR/libfoo.a'.  The $LIBDIR
 708                          * environment variable has never been supported or
 709                          * documented for our linker.  However, until this
 710                          * option is formally EOLed, we have to support it.
 711                          */
 712                         char *dir;
 713                         lib_node_t *node;
 714                         size_t len;
 715 
 716                         if ((dir = getenv("LIBDIR")) == NULL ||
 717                             dir[0] == '\0')
 718                                 dir = "/usr/lib";
 719                         node = safe_malloc(sizeof (lib_node_t));
 720                         len = strlen(optarg) + strlen(dir) + sizeof ("/lib.a");
 721                         node->path = safe_malloc(len);
 722 
 723                         (void) snprintf(node->path, len, "%s/lib%s.a", dir,
 724                             optarg);
 725                         node->next = libs;
 726                         libs = node;
 727                         break;
 728                 }
 729                 case 'L':
 730                         /*
 731                          * The '-L' option historically would attempt to read
 732                          * the .debug section of the target to determine source
 733                          * line information in order to annotate the output.
 734                          * No compiler has emitted these sections in many years,
 735                          * and the option has never done what it purported to
 736                          * do.  We silently consume the option for
 737                          * compatibility.
 738                          */
 739                         break;
 740                 case 'n':
 741                         g_numeric = 1;
 742                         break;
 743                 case 'o':
 744                         g_flags |= DIS_OCTAL;
 745                         break;
 746                 case 'q':
 747                         g_quiet = 1;
 748                         break;
 749                 case 't':
 750                         dis_namelist_add(g_seclist, optarg, DIS_TEXT);
 751                         break;
 752                 case 'V':
 753                         (void) printf("Solaris disassembler version 1.0\n");
 754                         return (0);
 755                 default:
 756                         usage();
 757                         break;
 758                 }
 759         }
 760 
 761         argc -= optind;
 762         argv += optind;
 763 
 764         if (argc == 0 && libs == NULL) {
 765                 warn("no objects specified");
 766                 usage();
 767         }
 768 
 769         if (dis_namelist_empty(g_funclist) && dis_namelist_empty(g_seclist))
 770                 g_doall = 1;
 771 
 772         /*
 773          * See comment for 'l' option, above.
 774          */
 775         while (libs != NULL) {
 776                 lib_node_t *node = libs->next;
 777 
 778                 dis_file(libs->path);
 779                 free(libs->path);
 780                 free(libs);
 781                 libs = node;
 782         }
 783 
 784         for (i = 0; i < argc; i++)
 785                 dis_file(argv[i]);
 786 
 787         dis_namelist_destroy(g_funclist);
 788         dis_namelist_destroy(g_seclist);
 789 
 790         return (g_error);
 791 }