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
getsymname(uint64_t addr,const char * symbol,off_t offset,char * buf,size_t buflen)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
insn_size(dis_handle_t * dhp)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
dis_data(dis_tgt_t * tgt,dis_handle_t * dhp,uint64_t addr,void * data,size_t datalen)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
do_lookup(void * data,uint64_t addr,char * buf,size_t buflen,uint64_t * start,size_t * symlen)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
do_read(void * data,uint64_t addr,void * buf,size_t len)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
dump_data(uint64_t addr,void * data,size_t datalen)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
dis_text_section(dis_tgt_t * tgt,dis_scn_t * scn,void * data)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
dis_named_section(dis_scn_t * scn,int type,void * data)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
dis_named_function(dis_func_t * func,int type,void * data)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
dis_file(const char * filename)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-exist 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
usage(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
main(int argc,char ** argv)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 }
792