1 /*-
2 * Copyright (c) 2007 Hyogeol Lee <hyogeollee@gmail.com>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer
10 * in this position and unchanged.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include <sys/queue.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <ar.h>
31 #include <assert.h>
32 #include <ctype.h>
33 #include <dwarf.h>
34 #include <err.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <gelf.h>
38 #include <getopt.h>
39 #include <inttypes.h>
40 #include <libdwarf.h>
41 #include <libelftc.h>
42 #include <stdbool.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <strings.h>
47 #include <unistd.h>
48
49 #include "_elftc.h"
50
51 ELFTC_VCSID("$Id: nm.c 3722 2019-03-23 17:01:58Z jkoshy $");
52
53 /* symbol information list */
54 STAILQ_HEAD(sym_head, sym_entry);
55
56 struct sym_entry {
57 char *name;
58 GElf_Sym *sym;
59 STAILQ_ENTRY(sym_entry) sym_entries;
60 };
61
62 typedef int (*fn_sort)(const void *, const void *);
63 typedef void (*fn_elem_print)(char, const char *, const GElf_Sym *, const char *);
64 typedef void (*fn_sym_print)(const GElf_Sym *);
65 typedef int (*fn_filter)(char, const GElf_Sym *, const char *);
66
67 /* output filter list */
68 static SLIST_HEAD(filter_head, filter_entry) nm_out_filter =
69 SLIST_HEAD_INITIALIZER(nm_out_filter);
70
71 struct filter_entry {
72 fn_filter fn;
73 SLIST_ENTRY(filter_entry) filter_entries;
74 };
75
76 struct sym_print_data {
77 struct sym_head *headp;
78 size_t sh_num, list_num;
79 const char *t_table, **s_table, *filename, *objname;
80 };
81
82 struct nm_prog_info {
83 const char *name;
84 const char *def_filename;
85 };
86
87 /* List for line number information. */
88 struct line_info_entry {
89 uint64_t addr; /* address */
90 uint64_t line; /* line number */
91 char *file; /* file name with path */
92 SLIST_ENTRY(line_info_entry) entries;
93 };
94 SLIST_HEAD(line_info_head, line_info_entry);
95
96 /* List for function line number information. */
97 struct func_info_entry {
98 char *name; /* function name */
99 char *file; /* file name with path */
100 uint64_t lowpc; /* low address */
101 uint64_t highpc; /* high address */
102 uint64_t line; /* line number */
103 SLIST_ENTRY(func_info_entry) entries;
104 };
105 SLIST_HEAD(func_info_head, func_info_entry);
106
107 /* List for variable line number information. */
108 struct var_info_entry {
109 char *name; /* variable name */
110 char *file; /* file name with path */
111 uint64_t addr; /* address */
112 uint64_t line; /* line number */
113 SLIST_ENTRY(var_info_entry) entries;
114 };
115 SLIST_HEAD(var_info_head, var_info_entry);
116
117 /* output numric type */
118 enum radix {
119 RADIX_OCT,
120 RADIX_HEX,
121 RADIX_DEC
122 };
123
124 /* output symbol type, PRINT_SYM_DYN for dynamic symbol only */
125 enum print_symbol {
126 PRINT_SYM_SYM,
127 PRINT_SYM_DYN
128 };
129
130 /* output name type */
131 enum print_name {
132 PRINT_NAME_NONE,
133 PRINT_NAME_FULL,
134 PRINT_NAME_MULTI
135 };
136
137 struct nm_prog_options {
138 enum print_symbol print_symbol;
139 enum print_name print_name;
140 enum radix t;
141 int demangle_type;
142 bool print_debug;
143 bool print_armap;
144 int print_size;
145 bool debug_line;
146 int def_only;
147 bool undef_only;
148 int sort_size;
149 bool sort_reverse;
150 int no_demangle;
151
152 /*
153 * function pointer to sort symbol list.
154 * possible function - cmp_name, cmp_none, cmp_size, cmp_value
155 */
156 fn_sort sort_fn;
157
158 /*
159 * function pointer to print symbol elem.
160 * possible function - sym_elem_print_all
161 * sym_elem_print_all_portable
162 * sym_elem_print_all_sysv
163 */
164 fn_elem_print elem_print_fn;
165
166 fn_sym_print value_print_fn;
167 fn_sym_print size_print_fn;
168 };
169
170 #define CHECK_SYM_PRINT_DATA(p) (p->headp == NULL || p->sh_num == 0 || \
171 p->t_table == NULL || p->s_table == NULL || p->filename == NULL)
172 #define IS_SYM_TYPE(t) ((t) == '?' || isalpha((t)) != 0)
173 #define IS_UNDEF_SYM_TYPE(t) ((t) == 'U' || (t) == 'v' || (t) == 'w')
174 #define UNUSED(p) ((void)p)
175
176 static int cmp_name(const void *, const void *);
177 static int cmp_none(const void *, const void *);
178 static int cmp_size(const void *, const void *);
179 static int cmp_value(const void *, const void *);
180 static void filter_dest(void);
181 static int filter_insert(fn_filter);
182 static void get_opt(int *, char ***);
183 static int get_sym(Elf *, struct sym_head *, int, size_t, size_t,
184 const char *, const char **, int);
185 static const char * get_sym_name(Elf *, const GElf_Sym *, size_t,
186 const char **, int);
187 static char get_sym_type(const GElf_Sym *, const char *);
188 static void global_dest(void);
189 static void global_init(void);
190 static bool is_sec_data(GElf_Shdr *);
191 static bool is_sec_debug(const char *);
192 static bool is_sec_nobits(GElf_Shdr *);
193 static bool is_sec_readonly(GElf_Shdr *);
194 static bool is_sec_text(GElf_Shdr *);
195 static void print_ar_index(int, Elf *);
196 static void print_header(const char *, const char *);
197 static void print_version(void);
198 static int read_elf(Elf *, const char *, Elf_Kind);
199 static int read_object(const char *);
200 static int read_files(int, char **);
201 static void set_opt_value_print_fn(enum radix);
202 static int sym_elem_def(char, const GElf_Sym *, const char *);
203 static int sym_elem_global(char, const GElf_Sym *, const char *);
204 static int sym_elem_global_static(char, const GElf_Sym *,
205 const char *);
206 static int sym_elem_nondebug(char, const GElf_Sym *, const char *);
207 static int sym_elem_nonzero_size(char, const GElf_Sym *,
208 const char *);
209 static void sym_elem_print_all(char, const char *,
210 const GElf_Sym *, const char *);
211 static void sym_elem_print_all_portable(char, const char *,
212 const GElf_Sym *, const char *);
213 static void sym_elem_print_all_sysv(char, const char *,
214 const GElf_Sym *, const char *);
215 static int sym_elem_undef(char, const GElf_Sym *, const char *);
216 static void sym_list_dest(struct sym_head *);
217 static int sym_list_insert(struct sym_head *, const char *,
218 const GElf_Sym *);
219 static void sym_list_print(struct sym_print_data *,
220 struct func_info_head *, struct var_info_head *,
221 struct line_info_head *);
222 static void sym_list_print_each(struct sym_entry *,
223 struct sym_print_data *, struct func_info_head *,
224 struct var_info_head *, struct line_info_head *);
225 static struct sym_entry *sym_list_sort(struct sym_print_data *);
226 static void sym_size_oct_print(const GElf_Sym *);
227 static void sym_size_hex_print(const GElf_Sym *);
228 static void sym_size_dec_print(const GElf_Sym *);
229 static void sym_value_oct_print(const GElf_Sym *);
230 static void sym_value_hex_print(const GElf_Sym *);
231 static void sym_value_dec_print(const GElf_Sym *);
232 static void usage(int);
233
234 static struct nm_prog_info nm_info;
235 static struct nm_prog_options nm_opts;
236 static int nm_elfclass;
237
238 /*
239 * Point to current sym_print_data to use portable qsort function.
240 * (e.g. There is no qsort_r function in NetBSD.)
241 *
242 * Using in sym_list_sort.
243 */
244 static struct sym_print_data *nm_print_data;
245
246 static const struct option nm_longopts[] = {
247 { "debug-syms", no_argument, NULL, 'a' },
248 { "defined-only", no_argument, &nm_opts.def_only, 1},
249 { "demangle", optional_argument, NULL, 'C' },
250 { "dynamic", no_argument, NULL, 'D' },
251 { "extern-only", no_argument, NULL, 'g' },
252 { "format", required_argument, NULL, 'F' },
253 { "help", no_argument, NULL, 'h' },
254 { "line-numbers", no_argument, NULL, 'l' },
255 { "no-demangle", no_argument, &nm_opts.no_demangle,
256 1},
257 { "no-sort", no_argument, NULL, 'p' },
258 { "numeric-sort", no_argument, NULL, 'v' },
259 { "print-armap", no_argument, NULL, 's' },
260 { "print-file-name", no_argument, NULL, 'A' },
261 { "print-size", no_argument, NULL, 'S' },
262 { "radix", required_argument, NULL, 't' },
263 { "reverse-sort", no_argument, NULL, 'r' },
264 { "size-sort", no_argument, &nm_opts.sort_size, 1},
265 { "undefined-only", no_argument, NULL, 'u' },
266 { "version", no_argument, NULL, 'V' },
267 { NULL, 0, NULL, 0 }
268 };
269
270 #if defined(ELFTC_NEED_BYTEORDER_EXTENSIONS)
271 static __inline uint32_t
be32dec(const void * pp)272 be32dec(const void *pp)
273 {
274 unsigned char const *p = (unsigned char const *)pp;
275
276 return ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
277 }
278
279 static __inline uint32_t
le32dec(const void * pp)280 le32dec(const void *pp)
281 {
282 unsigned char const *p = (unsigned char const *)pp;
283
284 return ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
285 }
286
287 static __inline uint64_t
be64dec(const void * pp)288 be64dec(const void *pp)
289 {
290 unsigned char const *p = (unsigned char const *)pp;
291
292 return (((uint64_t)be32dec(p) << 32) | be32dec(p + 4));
293 }
294
295 static __inline uint64_t
le64dec(const void * pp)296 le64dec(const void *pp)
297 {
298 unsigned char const *p = (unsigned char const *)pp;
299
300 return (((uint64_t)le32dec(p + 4) << 32) | le32dec(p));
301 }
302 #endif
303
304 static int
cmp_name(const void * l,const void * r)305 cmp_name(const void *l, const void *r)
306 {
307
308 assert(l != NULL);
309 assert(r != NULL);
310 assert(((const struct sym_entry *)l)->name != NULL);
311 assert(((const struct sym_entry *)r)->name != NULL);
312
313 return (strcmp(((const struct sym_entry *)l)->name,
314 ((const struct sym_entry *)r)->name));
315 }
316
317 static int
cmp_none(const void * l,const void * r)318 cmp_none(const void *l, const void *r)
319 {
320
321 UNUSED(l);
322 UNUSED(r);
323
324 return (0);
325 }
326
327 /* Size comparison. If l and r have same size, compare their name. */
328 static int
cmp_size(const void * lp,const void * rp)329 cmp_size(const void *lp, const void *rp)
330 {
331 const struct sym_entry *l, *r;
332
333 l = lp;
334 r = rp;
335
336 assert(l != NULL);
337 assert(l->name != NULL);
338 assert(l->sym != NULL);
339 assert(r != NULL);
340 assert(r->name != NULL);
341 assert(r->sym != NULL);
342
343 if (l->sym->st_size == r->sym->st_size)
344 return (strcmp(l->name, r->name));
345
346 return (l->sym->st_size - r->sym->st_size);
347 }
348
349 /* Value comparison. Undefined symbols come first. */
350 static int
cmp_value(const void * lp,const void * rp)351 cmp_value(const void *lp, const void *rp)
352 {
353 const struct sym_entry *l, *r;
354 const char *ttable;
355 int l_is_undef, r_is_undef;
356
357 l = lp;
358 r = rp;
359
360 assert(nm_print_data != NULL);
361 ttable = nm_print_data->t_table;
362
363 assert(l != NULL);
364 assert(l->name != NULL);
365 assert(l->sym != NULL);
366 assert(r != NULL);
367 assert(r->name != NULL);
368 assert(r->sym != NULL);
369 assert(ttable != NULL);
370
371 l_is_undef = IS_UNDEF_SYM_TYPE(get_sym_type(l->sym, ttable)) ? 1 : 0;
372 r_is_undef = IS_UNDEF_SYM_TYPE(get_sym_type(r->sym, ttable)) ? 1 : 0;
373
374 assert(l_is_undef + r_is_undef >= 0);
375 assert(l_is_undef + r_is_undef <= 2);
376
377 switch (l_is_undef + r_is_undef) {
378 case 0:
379 /* Both defined */
380 if (l->sym->st_value == r->sym->st_value)
381 return (strcmp(l->name, r->name));
382 return (l->sym->st_value > r->sym->st_value ? 1 : -1);
383 case 1:
384 /* One undefined */
385 return (l_is_undef == 0 ? 1 : -1);
386 case 2:
387 /* Both undefined */
388 return (strcmp(l->name, r->name));
389 }
390 /* NOTREACHED */
391
392 return (l->sym->st_value - r->sym->st_value);
393 }
394
395 static void
filter_dest(void)396 filter_dest(void)
397 {
398 struct filter_entry *e;
399
400 while (!SLIST_EMPTY(&nm_out_filter)) {
401 e = SLIST_FIRST(&nm_out_filter);
402 SLIST_REMOVE_HEAD(&nm_out_filter, filter_entries);
403 free(e);
404 }
405 }
406
407 static int
filter_insert(fn_filter filter_fn)408 filter_insert(fn_filter filter_fn)
409 {
410 struct filter_entry *e;
411
412 assert(filter_fn != NULL);
413
414 if ((e = malloc(sizeof(struct filter_entry))) == NULL) {
415 warn("malloc");
416 return (0);
417 }
418 e->fn = filter_fn;
419 SLIST_INSERT_HEAD(&nm_out_filter, e, filter_entries);
420
421 return (1);
422 }
423
424 static int
parse_demangle_option(const char * opt)425 parse_demangle_option(const char *opt)
426 {
427
428 if (opt == NULL)
429 return (ELFTC_DEM_UNKNOWN);
430 else if (!strncasecmp(opt, "gnu-v2", 6))
431 return (ELFTC_DEM_GNU2);
432 else if (!strncasecmp(opt, "gnu-v3", 6))
433 return (ELFTC_DEM_GNU3);
434 else if (!strncasecmp(opt, "arm", 3))
435 return (ELFTC_DEM_ARM);
436 else
437 errx(EXIT_FAILURE, "unknown demangling style '%s'", opt);
438
439 /* NOTREACHED */
440 return (0);
441 }
442
443 static void
get_opt(int * argc,char *** argv)444 get_opt(int *argc, char ***argv)
445 {
446 int ch;
447 bool is_posix, oflag;
448
449 if (*argc <= 0 || *argv == NULL)
450 return;
451
452 oflag = is_posix = false;
453 nm_opts.t = RADIX_HEX;
454 while ((ch = getopt_long(*argc, *argv, "ABCDF:PSVaefghlnoprst:uvx",
455 nm_longopts, NULL)) != -1) {
456 switch (ch) {
457 case 'A':
458 nm_opts.print_name = PRINT_NAME_FULL;
459 break;
460 case 'B':
461 nm_opts.elem_print_fn = &sym_elem_print_all;
462 break;
463 case 'C':
464 nm_opts.demangle_type = parse_demangle_option(optarg);
465 break;
466 case 'D':
467 nm_opts.print_symbol = PRINT_SYM_DYN;
468 break;
469 case 'F':
470 /* sysv, bsd, posix */
471 switch (optarg[0]) {
472 case 'B':
473 case 'b':
474 nm_opts.elem_print_fn = &sym_elem_print_all;
475 break;
476 case 'P':
477 case 'p':
478 is_posix = true;
479 nm_opts.elem_print_fn =
480 &sym_elem_print_all_portable;
481 break;
482 case 'S':
483 case 's':
484 nm_opts.elem_print_fn =
485 &sym_elem_print_all_sysv;
486 break;
487 default:
488 warnx("%s: Invalid format", optarg);
489 usage(1);
490 }
491
492 break;
493 case 'P':
494 is_posix = true;
495 nm_opts.elem_print_fn = &sym_elem_print_all_portable;
496 break;
497 case 'S':
498 nm_opts.print_size = 1;
499 break;
500 case 'V':
501 print_version();
502 /* NOTREACHED */
503 case 'a':
504 nm_opts.print_debug = true;
505 break;
506 case 'e':
507 filter_insert(sym_elem_global_static);
508 break;
509 case 'f':
510 break;
511 case 'g':
512 filter_insert(sym_elem_global);
513 break;
514 case 'h':
515 usage(0);
516 break;
517 case 'l':
518 nm_opts.debug_line = true;
519 break;
520 case 'n':
521 case 'v':
522 nm_opts.sort_fn = &cmp_value;
523 break;
524 case 'o':
525 oflag = true;
526 break;
527 case 'p':
528 nm_opts.sort_fn = &cmp_none;
529 break;
530 case 'r':
531 nm_opts.sort_reverse = true;
532 break;
533 case 's':
534 nm_opts.print_armap = true;
535 break;
536 case 't':
537 /* t require always argument to getopt_long */
538 switch (optarg[0]) {
539 case 'd':
540 nm_opts.t = RADIX_DEC;
541 break;
542 case 'o':
543 nm_opts.t = RADIX_OCT;
544 break;
545 case 'x':
546 nm_opts.t = RADIX_HEX;
547 break;
548 default:
549 warnx("%s: Invalid radix", optarg);
550 usage(1);
551 }
552 break;
553 case 'u':
554 filter_insert(sym_elem_undef);
555 nm_opts.undef_only = true;
556 break;
557 /* case 'v': see case 'n' above. */
558 case 'x':
559 nm_opts.t = RADIX_HEX;
560 break;
561 case 0:
562 if (nm_opts.sort_size != 0) {
563 nm_opts.sort_fn = &cmp_size;
564 filter_insert(sym_elem_def);
565 filter_insert(sym_elem_nonzero_size);
566 }
567 if (nm_opts.def_only != 0)
568 filter_insert(sym_elem_def);
569 if (nm_opts.no_demangle != 0)
570 nm_opts.demangle_type = -1;
571 break;
572 default :
573 usage(1);
574 }
575 }
576 *argc -= optind;
577 *argv += optind;
578
579 /*
580 * In POSIX mode, the '-o' option controls the output radix.
581 * In non-POSIX mode, the option is a synonym for the '-A' and
582 * '--print-file-name' options.
583 */
584 if (oflag) {
585 if (is_posix)
586 nm_opts.t = RADIX_OCT;
587 else
588 nm_opts.print_name = PRINT_NAME_FULL;
589 }
590
591 assert(nm_opts.sort_fn != NULL && "nm_opts.sort_fn is null");
592 assert(nm_opts.elem_print_fn != NULL &&
593 "nm_opts.elem_print_fn is null");
594 assert(nm_opts.value_print_fn != NULL &&
595 "nm_opts.value_print_fn is null");
596
597 set_opt_value_print_fn(nm_opts.t);
598
599 if (nm_opts.undef_only == true) {
600 if (nm_opts.sort_fn == &cmp_size)
601 errx(EXIT_FAILURE,
602 "--size-sort with -u is meaningless");
603 if (nm_opts.def_only != 0)
604 errx(EXIT_FAILURE,
605 "-u with --defined-only is meaningless");
606 }
607 if (nm_opts.print_debug == false)
608 filter_insert(sym_elem_nondebug);
609 if (nm_opts.sort_reverse == true && nm_opts.sort_fn == cmp_none)
610 nm_opts.sort_reverse = false;
611 }
612
613 /*
614 * Get symbol information from elf.
615 */
616 static int
get_sym(Elf * elf,struct sym_head * headp,int shnum,size_t dynndx,size_t strndx,const char * type_table,const char ** sec_table,int sec_table_size)617 get_sym(Elf *elf, struct sym_head *headp, int shnum, size_t dynndx,
618 size_t strndx, const char *type_table, const char **sec_table,
619 int sec_table_size)
620 {
621 Elf_Scn *scn;
622 Elf_Data *data;
623 GElf_Shdr shdr;
624 GElf_Sym sym;
625 struct filter_entry *fep;
626 size_t ndx;
627 int rtn;
628 const char *sym_name;
629 char type;
630 bool filter;
631 int i, j;
632
633 assert(elf != NULL);
634 assert(headp != NULL);
635
636 rtn = 0;
637 for (i = 1; i < shnum; i++) {
638 if ((scn = elf_getscn(elf, i)) == NULL) {
639 warnx("elf_getscn failed: %s", elf_errmsg(-1));
640 continue;
641 }
642 if (gelf_getshdr(scn, &shdr) != &shdr) {
643 warnx("gelf_getshdr failed: %s", elf_errmsg(-1));
644 continue;
645 }
646 if (shdr.sh_type == SHT_SYMTAB) {
647 if (nm_opts.print_symbol != PRINT_SYM_SYM)
648 continue;
649 } else if (shdr.sh_type == SHT_DYNSYM) {
650 if (nm_opts.print_symbol != PRINT_SYM_DYN)
651 continue;
652 } else
653 continue;
654
655 ndx = shdr.sh_type == SHT_DYNSYM ? dynndx : strndx;
656
657 data = NULL;
658 while ((data = elf_getdata(scn, data)) != NULL) {
659 j = 1;
660 while (gelf_getsym(data, j++, &sym) != NULL) {
661 sym_name = get_sym_name(elf, &sym, ndx,
662 sec_table, sec_table_size);
663 filter = false;
664 type = get_sym_type(&sym, type_table);
665 SLIST_FOREACH(fep, &nm_out_filter,
666 filter_entries) {
667 if (!fep->fn(type, &sym, sym_name)) {
668 filter = true;
669 break;
670 }
671 }
672 if (filter == false) {
673 if (sym_list_insert(headp, sym_name,
674 &sym) == 0)
675 return (0);
676 rtn++;
677 }
678 }
679 }
680 }
681
682 return (rtn);
683 }
684
685 static const char *
get_sym_name(Elf * elf,const GElf_Sym * sym,size_t ndx,const char ** sec_table,int sec_table_size)686 get_sym_name(Elf *elf, const GElf_Sym *sym, size_t ndx, const char **sec_table,
687 int sec_table_size)
688 {
689 const char *sym_name;
690
691 sym_name = NULL;
692
693 /* Show section name as symbol name for STT_SECTION symbols. */
694 if (GELF_ST_TYPE(sym->st_info) == STT_SECTION) {
695 if (sec_table != NULL && sym->st_shndx < sec_table_size)
696 sym_name = sec_table[sym->st_shndx];
697 } else
698 sym_name = elf_strptr(elf, ndx, sym->st_name);
699
700 if (sym_name == NULL)
701 sym_name = "(null)";
702
703 return (sym_name);
704 }
705
706 static char
get_sym_type(const GElf_Sym * sym,const char * type_table)707 get_sym_type(const GElf_Sym *sym, const char *type_table)
708 {
709 bool is_local;
710
711 if (sym == NULL || type_table == NULL)
712 return ('?');
713
714 is_local = sym->st_info >> 4 == STB_LOCAL;
715
716 if (sym->st_shndx == SHN_ABS) /* absolute */
717 return (is_local ? 'a' : 'A');
718
719 if (sym->st_shndx == SHN_COMMON) /* common */
720 return ('C');
721
722 if ((sym->st_info) >> 4 == STB_WEAK) { /* weak */
723 if ((sym->st_info & 0xf) == STT_OBJECT)
724 return (sym->st_shndx == SHN_UNDEF ? 'v' : 'V');
725
726 return (sym->st_shndx == SHN_UNDEF ? 'w' : 'W');
727 }
728
729 if (sym->st_shndx == SHN_UNDEF) /* undefined */
730 return ('U');
731
732 return (is_local == true && type_table[sym->st_shndx] != 'N' ?
733 tolower((unsigned char) type_table[sym->st_shndx]) :
734 type_table[sym->st_shndx]);
735 }
736
737 static void
global_dest(void)738 global_dest(void)
739 {
740
741 filter_dest();
742 }
743
744 static void
global_init(void)745 global_init(void)
746 {
747
748 if (elf_version(EV_CURRENT) == EV_NONE)
749 errx(EXIT_FAILURE, "elf_version error");
750
751 nm_info.name = ELFTC_GETPROGNAME();
752 nm_info.def_filename = "a.out";
753 nm_opts.print_symbol = PRINT_SYM_SYM;
754 nm_opts.print_name = PRINT_NAME_NONE;
755 nm_opts.demangle_type = -1;
756 nm_opts.print_debug = false;
757 nm_opts.print_armap = false;
758 nm_opts.print_size = 0;
759 nm_opts.debug_line = false;
760 nm_opts.def_only = 0;
761 nm_opts.undef_only = false;
762 nm_opts.sort_size = 0;
763 nm_opts.sort_reverse = false;
764 nm_opts.no_demangle = 0;
765 nm_opts.sort_fn = &cmp_name;
766 nm_opts.elem_print_fn = &sym_elem_print_all;
767 nm_opts.value_print_fn = &sym_value_dec_print;
768 nm_opts.size_print_fn = &sym_size_dec_print;
769 SLIST_INIT(&nm_out_filter);
770 }
771
772 static bool
is_sec_data(GElf_Shdr * s)773 is_sec_data(GElf_Shdr *s)
774 {
775
776 assert(s != NULL && "shdr is NULL");
777
778 return (((s->sh_flags & SHF_ALLOC) != 0) && s->sh_type != SHT_NOBITS);
779 }
780
781 static bool
is_sec_debug(const char * shname)782 is_sec_debug(const char *shname)
783 {
784 const char *dbg_sec[] = {
785 ".debug",
786 ".gnu.linkonce.wi.",
787 ".line",
788 ".rel.debug",
789 ".rela.debug",
790 ".stab",
791 NULL
792 };
793 const char **p;
794
795 if (shname == NULL)
796 return (false);
797
798 for (p = dbg_sec; *p; p++) {
799 if (!strncmp(shname, *p, strlen(*p)))
800 return (true);
801 }
802
803 return (false);
804 }
805
806 static bool
is_sec_nobits(GElf_Shdr * s)807 is_sec_nobits(GElf_Shdr *s)
808 {
809
810 assert(s != NULL && "shdr is NULL");
811
812 return (s->sh_type == SHT_NOBITS);
813 }
814
815 static bool
is_sec_readonly(GElf_Shdr * s)816 is_sec_readonly(GElf_Shdr *s)
817 {
818
819 assert(s != NULL && "shdr is NULL");
820
821 return ((s->sh_flags & SHF_WRITE) == 0);
822 }
823
824 static bool
is_sec_text(GElf_Shdr * s)825 is_sec_text(GElf_Shdr *s)
826 {
827
828 assert(s != NULL && "shdr is NULL");
829
830 return ((s->sh_flags & SHF_EXECINSTR) != 0);
831 }
832
833 static void
print_ar_index(int fd,Elf * arf)834 print_ar_index(int fd, Elf *arf)
835 {
836 Elf *elf;
837 Elf_Arhdr *arhdr;
838 Elf_Arsym *arsym;
839 Elf_Cmd cmd;
840 off_t start;
841 size_t arsym_size;
842
843 if (arf == NULL)
844 return;
845
846 if ((arsym = elf_getarsym(arf, &arsym_size)) == NULL)
847 return;
848
849 printf("\nArchive index:\n");
850
851 start = arsym->as_off;
852 cmd = ELF_C_READ;
853 while (arsym_size > 1) {
854 if (elf_rand(arf, arsym->as_off) == arsym->as_off &&
855 (elf = elf_begin(fd, cmd, arf)) != NULL) {
856 if ((arhdr = elf_getarhdr(elf)) != NULL)
857 printf("%s in %s\n", arsym->as_name,
858 arhdr->ar_name != NULL ?
859 arhdr->ar_name : arhdr->ar_rawname);
860 elf_end(elf);
861 }
862 ++arsym;
863 --arsym_size;
864 }
865
866 elf_rand(arf, start);
867 }
868
869 #define DEMANGLED_BUFFER_SIZE (8 * 1024)
870 #define PRINT_DEMANGLED_NAME(FORMAT, NAME) do { \
871 char _demangled[DEMANGLED_BUFFER_SIZE]; \
872 if (nm_opts.demangle_type < 0 || \
873 elftc_demangle((NAME), _demangled, sizeof(_demangled), \
874 nm_opts.demangle_type) < 0) \
875 printf((FORMAT), (NAME)); \
876 else \
877 printf((FORMAT), _demangled); \
878 } while (0)
879
880 static void
print_header(const char * file,const char * obj)881 print_header(const char *file, const char *obj)
882 {
883
884 if (file == NULL)
885 return;
886
887 if (nm_opts.elem_print_fn == &sym_elem_print_all_sysv) {
888 printf("\n\n%s from %s",
889 nm_opts.undef_only == false ? "Symbols" :
890 "Undefined symbols", file);
891 if (obj != NULL)
892 printf("[%s]", obj);
893 printf(":\n\n");
894
895 printf("\
896 Name Value Class Type Size Line Section\n\n");
897 } else {
898 /* archive file without -A option and POSIX */
899 if (nm_opts.print_name != PRINT_NAME_FULL && obj != NULL) {
900 if (nm_opts.elem_print_fn ==
901 sym_elem_print_all_portable)
902 printf("%s[%s]:\n", file, obj);
903 else if (nm_opts.elem_print_fn == sym_elem_print_all)
904 printf("\n%s:\n", obj);
905 /* multiple files(not archive) without -A option */
906 } else if (nm_opts.print_name == PRINT_NAME_MULTI) {
907 if (nm_opts.elem_print_fn == sym_elem_print_all)
908 printf("\n");
909 printf("%s:\n", file);
910 }
911 }
912 }
913
914 static void
print_version(void)915 print_version(void)
916 {
917
918 (void) printf("%s (%s)\n", nm_info.name, elftc_version());
919 exit(0);
920 }
921
922 static uint64_t
get_block_value(Dwarf_Debug dbg,Dwarf_Block * block)923 get_block_value(Dwarf_Debug dbg, Dwarf_Block *block)
924 {
925 Elf *elf;
926 GElf_Ehdr eh;
927 Dwarf_Error de;
928
929 if (dwarf_get_elf(dbg, &elf, &de) != DW_DLV_OK) {
930 warnx("dwarf_get_elf failed: %s", dwarf_errmsg(de));
931 return (0);
932 }
933
934 if (gelf_getehdr(elf, &eh) != &eh) {
935 warnx("gelf_getehdr failed: %s", elf_errmsg(-1));
936 return (0);
937 }
938
939 if (block->bl_len == 5) {
940 if (eh.e_ident[EI_DATA] == ELFDATA2LSB)
941 return (le32dec((uint8_t *) block->bl_data + 1));
942 else
943 return (be32dec((uint8_t *) block->bl_data + 1));
944 } else if (block->bl_len == 9) {
945 if (eh.e_ident[EI_DATA] == ELFDATA2LSB)
946 return (le64dec((uint8_t *) block->bl_data + 1));
947 else
948 return (be64dec((uint8_t *) block->bl_data + 1));
949 }
950
951 return (0);
952 }
953
954 static char *
find_object_name(Dwarf_Debug dbg,Dwarf_Die die)955 find_object_name(Dwarf_Debug dbg, Dwarf_Die die)
956 {
957 Dwarf_Die ret_die;
958 Dwarf_Attribute at;
959 Dwarf_Off off;
960 Dwarf_Error de;
961 const char *str;
962 char *name;
963
964 if (dwarf_attrval_string(die, DW_AT_name, &str, &de) == DW_DLV_OK) {
965 if ((name = strdup(str)) == NULL) {
966 warn("strdup");
967 return (NULL);
968 }
969 return (name);
970 }
971
972 if (dwarf_attr(die, DW_AT_specification, &at, &de) != DW_DLV_OK)
973 return (NULL);
974
975 if (dwarf_global_formref(at, &off, &de) != DW_DLV_OK)
976 return (NULL);
977
978 if (dwarf_offdie(dbg, off, &ret_die, &de) != DW_DLV_OK)
979 return (NULL);
980
981 return (find_object_name(dbg, ret_die));
982 }
983
984 static void
search_line_attr(Dwarf_Debug dbg,struct func_info_head * func_info,struct var_info_head * var_info,Dwarf_Die die,char ** src_files,Dwarf_Signed filecount)985 search_line_attr(Dwarf_Debug dbg, struct func_info_head *func_info,
986 struct var_info_head *var_info, Dwarf_Die die, char **src_files,
987 Dwarf_Signed filecount)
988 {
989 Dwarf_Attribute at;
990 Dwarf_Unsigned udata;
991 Dwarf_Half tag;
992 Dwarf_Block *block;
993 Dwarf_Bool flag;
994 Dwarf_Die ret_die;
995 Dwarf_Error de;
996 struct func_info_entry *func;
997 struct var_info_entry *var;
998 int ret;
999
1000 if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) {
1001 warnx("dwarf_tag failed: %s", dwarf_errmsg(de));
1002 goto cont_search;
1003 }
1004
1005 /* We're interested in DIEs which define functions or variables. */
1006 if (tag != DW_TAG_subprogram && tag != DW_TAG_entry_point &&
1007 tag != DW_TAG_inlined_subroutine && tag != DW_TAG_variable)
1008 goto cont_search;
1009
1010 if (tag == DW_TAG_variable) {
1011
1012 /* Ignore "artificial" variable. */
1013 if (dwarf_attrval_flag(die, DW_AT_artificial, &flag, &de) ==
1014 DW_DLV_OK && flag)
1015 goto cont_search;
1016
1017 /* Ignore pure declaration. */
1018 if (dwarf_attrval_flag(die, DW_AT_declaration, &flag, &de) ==
1019 DW_DLV_OK && flag)
1020 goto cont_search;
1021
1022 /* Ignore stack varaibles. */
1023 if (dwarf_attrval_flag(die, DW_AT_external, &flag, &de) !=
1024 DW_DLV_OK || !flag)
1025 goto cont_search;
1026
1027 if ((var = calloc(1, sizeof(*var))) == NULL) {
1028 warn("calloc failed");
1029 goto cont_search;
1030 }
1031
1032 if (dwarf_attrval_unsigned(die, DW_AT_decl_file, &udata,
1033 &de) == DW_DLV_OK && udata > 0 &&
1034 (Dwarf_Signed) (udata - 1) < filecount) {
1035 var->file = strdup(src_files[udata - 1]);
1036 if (var->file == NULL) {
1037 warn("strdup");
1038 free(var);
1039 goto cont_search;
1040 }
1041 }
1042
1043 if (dwarf_attrval_unsigned(die, DW_AT_decl_line, &udata, &de) ==
1044 DW_DLV_OK)
1045 var->line = udata;
1046
1047 var->name = find_object_name(dbg, die);
1048 if (var->name == NULL) {
1049 if (var->file)
1050 free(var->file);
1051 free(var);
1052 goto cont_search;
1053 }
1054
1055 if (dwarf_attr(die, DW_AT_location, &at, &de) == DW_DLV_OK &&
1056 dwarf_formblock(at, &block, &de) == DW_DLV_OK) {
1057 /*
1058 * Since we ignored stack variables, the rest are the
1059 * external varaibles which should always use DW_OP_addr
1060 * operator for DW_AT_location value.
1061 */
1062 if (*((uint8_t *)block->bl_data) == DW_OP_addr)
1063 var->addr = get_block_value(dbg, block);
1064 }
1065
1066 SLIST_INSERT_HEAD(var_info, var, entries);
1067
1068 } else {
1069
1070 if ((func = calloc(1, sizeof(*func))) == NULL) {
1071 warn("calloc failed");
1072 goto cont_search;
1073 }
1074
1075 /*
1076 * Note that dwarf_attrval_unsigned() handles DW_AT_abstract_origin
1077 * internally, so it can retrieve DW_AT_decl_file/DW_AT_decl_line
1078 * attributes for inlined functions as well.
1079 */
1080 if (dwarf_attrval_unsigned(die, DW_AT_decl_file, &udata,
1081 &de) == DW_DLV_OK && udata > 0 &&
1082 (Dwarf_Signed) (udata - 1) < filecount) {
1083 func->file = strdup(src_files[udata - 1]);
1084 if (func->file == NULL) {
1085 warn("strdup");
1086 free(func);
1087 goto cont_search;
1088 }
1089 }
1090
1091 if (dwarf_attrval_unsigned(die, DW_AT_decl_line, &udata, &de) ==
1092 DW_DLV_OK)
1093 func->line = udata;
1094
1095 func->name = find_object_name(dbg, die);
1096 if (func->name == NULL) {
1097 if (func->file)
1098 free(func->file);
1099 free(func);
1100 goto cont_search;
1101 }
1102
1103 if (dwarf_attrval_unsigned(die, DW_AT_low_pc, &udata, &de) ==
1104 DW_DLV_OK)
1105 func->lowpc = udata;
1106 if (dwarf_attrval_unsigned(die, DW_AT_high_pc, &udata, &de) ==
1107 DW_DLV_OK)
1108 func->highpc = udata;
1109
1110 SLIST_INSERT_HEAD(func_info, func, entries);
1111 }
1112
1113 cont_search:
1114
1115 /* Search children. */
1116 ret = dwarf_child(die, &ret_die, &de);
1117 if (ret == DW_DLV_ERROR)
1118 warnx("dwarf_child: %s", dwarf_errmsg(de));
1119 else if (ret == DW_DLV_OK)
1120 search_line_attr(dbg, func_info, var_info, ret_die, src_files,
1121 filecount);
1122
1123 /* Search sibling. */
1124 ret = dwarf_siblingof(dbg, die, &ret_die, &de);
1125 if (ret == DW_DLV_ERROR)
1126 warnx("dwarf_siblingof: %s", dwarf_errmsg(de));
1127 else if (ret == DW_DLV_OK)
1128 search_line_attr(dbg, func_info, var_info, ret_die, src_files,
1129 filecount);
1130
1131 dwarf_dealloc(dbg, die, DW_DLA_DIE);
1132 }
1133
1134 /*
1135 * Read elf file and collect symbol information, sort them, print.
1136 * Return 1 at failed, 0 at success.
1137 */
1138 static int
read_elf(Elf * elf,const char * filename,Elf_Kind kind)1139 read_elf(Elf *elf, const char *filename, Elf_Kind kind)
1140 {
1141 Dwarf_Debug dbg;
1142 Dwarf_Die die;
1143 Dwarf_Error de;
1144 Dwarf_Half tag;
1145 Elf_Arhdr *arhdr;
1146 Elf_Scn *scn;
1147 GElf_Shdr shdr;
1148 Dwarf_Line *lbuf;
1149 Dwarf_Unsigned lineno;
1150 Dwarf_Signed lcount, filecount;
1151 Dwarf_Addr lineaddr;
1152 struct sym_print_data p_data;
1153 struct sym_head list_head;
1154 struct line_info_head *line_info;
1155 struct func_info_head *func_info;
1156 struct var_info_head *var_info;
1157 struct line_info_entry *lie;
1158 struct func_info_entry *func;
1159 struct var_info_entry *var;
1160 const char *shname, *objname;
1161 char *type_table, **sec_table, *sfile, **src_files;
1162 size_t i, shstrndx, shnum, dynndx, strndx;
1163 int ret, rtn, e_err;
1164
1165 #define OBJNAME (objname == NULL ? filename : objname)
1166
1167 assert(filename != NULL && "filename is null");
1168
1169 STAILQ_INIT(&list_head);
1170 type_table = NULL;
1171 sec_table = NULL;
1172 line_info = NULL;
1173 func_info = NULL;
1174 var_info = NULL;
1175 objname = NULL;
1176 dynndx = SHN_UNDEF;
1177 strndx = SHN_UNDEF;
1178 rtn = 0;
1179
1180 nm_elfclass = gelf_getclass(elf);
1181
1182 if (kind == ELF_K_AR) {
1183 if ((arhdr = elf_getarhdr(elf)) == NULL)
1184 goto next_cmd;
1185 objname = arhdr->ar_name != NULL ? arhdr->ar_name :
1186 arhdr->ar_rawname;
1187 }
1188 if (!elf_getshnum(elf, &shnum)) {
1189 if ((e_err = elf_errno()) != 0)
1190 warnx("%s: %s", OBJNAME, "File format not recognized");
1191 else
1192 warnx("%s: cannot get section number", OBJNAME);
1193 rtn = 1;
1194 goto next_cmd;
1195 }
1196 if (shnum == 0) {
1197 warnx("%s: has no section", OBJNAME);
1198 rtn = 1;
1199 goto next_cmd;
1200 }
1201 if (!elf_getshstrndx(elf, &shstrndx)) {
1202 warnx("%s: cannot get str index", OBJNAME);
1203 rtn = 1;
1204 goto next_cmd;
1205 }
1206 /* type_table for type determine */
1207 if ((type_table = malloc(sizeof(char) * shnum)) == NULL) {
1208 warn("%s: malloc", OBJNAME);
1209 rtn = 1;
1210 goto next_cmd;
1211 }
1212 /* sec_table for section name to display in sysv format */
1213 if ((sec_table = calloc(shnum, sizeof(char *))) == NULL) {
1214 warn("%s: calloc", OBJNAME);
1215 rtn = 1;
1216 goto next_cmd;
1217 }
1218
1219 type_table[0] = 'U';
1220 if ((sec_table[0] = strdup("*UND*")) == NULL) {
1221 warn("strdup");
1222 goto next_cmd;
1223 }
1224
1225 for (i = 1; i < shnum; ++i) {
1226 type_table[i] = 'U';
1227 if ((scn = elf_getscn(elf, i)) == NULL) {
1228 if ((e_err = elf_errno()) != 0)
1229 warnx("%s: %s", OBJNAME, elf_errmsg(e_err));
1230 else
1231 warnx("%s: cannot get section", OBJNAME);
1232 rtn = 1;
1233 goto next_cmd;
1234 }
1235 if (gelf_getshdr(scn, &shdr) == NULL)
1236 goto next_cmd;
1237
1238 /*
1239 * Cannot test by type and attribute for dynstr, strtab
1240 */
1241 shname = elf_strptr(elf, shstrndx, (size_t) shdr.sh_name);
1242 if (shname != NULL) {
1243 if ((sec_table[i] = strdup(shname)) == NULL) {
1244 warn("strdup");
1245 goto next_cmd;
1246 }
1247 if (!strncmp(shname, ".dynstr", 7)) {
1248 dynndx = elf_ndxscn(scn);
1249 if (dynndx == SHN_UNDEF) {
1250 warnx("%s: elf_ndxscn failed: %s",
1251 OBJNAME, elf_errmsg(-1));
1252 goto next_cmd;
1253 }
1254 }
1255 if (!strncmp(shname, ".strtab", 7)) {
1256 strndx = elf_ndxscn(scn);
1257 if (strndx == SHN_UNDEF) {
1258 warnx("%s: elf_ndxscn failed: %s",
1259 OBJNAME, elf_errmsg(-1));
1260 goto next_cmd;
1261 }
1262 }
1263 } else {
1264 sec_table[i] = strdup("*UND*");
1265 if (sec_table[i] == NULL) {
1266 warn("strdup");
1267 goto next_cmd;
1268 }
1269 }
1270
1271
1272 if (is_sec_text(&shdr))
1273 type_table[i] = 'T';
1274 else if (is_sec_data(&shdr)) {
1275 if (is_sec_readonly(&shdr))
1276 type_table[i] = 'R';
1277 else
1278 type_table[i] = 'D';
1279 } else if (is_sec_nobits(&shdr))
1280 type_table[i] = 'B';
1281 else if (is_sec_debug(shname))
1282 type_table[i] = 'N';
1283 else if (is_sec_readonly(&shdr) && !is_sec_nobits(&shdr))
1284 type_table[i] = 'n';
1285 }
1286
1287 print_header(filename, objname);
1288
1289 if ((dynndx == SHN_UNDEF && nm_opts.print_symbol == PRINT_SYM_DYN) ||
1290 (strndx == SHN_UNDEF && nm_opts.print_symbol == PRINT_SYM_SYM)) {
1291 warnx("%s: no symbols", OBJNAME);
1292 /* This is not an error case */
1293 goto next_cmd;
1294 }
1295
1296 STAILQ_INIT(&list_head);
1297
1298 if (!nm_opts.debug_line)
1299 goto process_sym;
1300
1301 /*
1302 * Collect dwarf line number information.
1303 */
1304
1305 if (dwarf_elf_init(elf, DW_DLC_READ, NULL, NULL, &dbg, &de) !=
1306 DW_DLV_OK) {
1307 warnx("dwarf_elf_init failed: %s", dwarf_errmsg(de));
1308 goto process_sym;
1309 }
1310
1311 line_info = malloc(sizeof(struct line_info_head));
1312 func_info = malloc(sizeof(struct func_info_head));
1313 var_info = malloc(sizeof(struct var_info_head));
1314 if (line_info != NULL)
1315 SLIST_INIT(line_info);
1316 if (func_info != NULL)
1317 SLIST_INIT(func_info);
1318 if (var_info != NULL)
1319 SLIST_INIT(var_info);
1320 if (line_info == NULL || func_info == NULL || var_info == NULL) {
1321 warn("malloc");
1322 (void) dwarf_finish(dbg, &de);
1323 goto process_sym;
1324 }
1325
1326 while ((ret = dwarf_next_cu_header(dbg, NULL, NULL, NULL, NULL, NULL,
1327 &de)) == DW_DLV_OK) {
1328 die = NULL;
1329 while (dwarf_siblingof(dbg, die, &die, &de) == DW_DLV_OK) {
1330 if (dwarf_tag(die, &tag, &de) != DW_DLV_OK) {
1331 warnx("dwarf_tag failed: %s",
1332 dwarf_errmsg(de));
1333 continue;
1334 }
1335 /* XXX: What about DW_TAG_partial_unit? */
1336 if (tag == DW_TAG_compile_unit)
1337 break;
1338 }
1339 if (die == NULL) {
1340 warnx("could not find DW_TAG_compile_unit die");
1341 continue;
1342 }
1343
1344 /* Retrieve source file list. */
1345 ret = dwarf_srcfiles(die, &src_files, &filecount, &de);
1346 if (ret == DW_DLV_ERROR)
1347 warnx("dwarf_srclines: %s", dwarf_errmsg(de));
1348 if (ret != DW_DLV_OK)
1349 continue;
1350
1351 /*
1352 * Retrieve line number information from .debug_line section.
1353 */
1354
1355 ret = dwarf_srclines(die, &lbuf, &lcount, &de);
1356 if (ret == DW_DLV_ERROR)
1357 warnx("dwarf_srclines: %s", dwarf_errmsg(de));
1358 if (ret != DW_DLV_OK)
1359 goto line_attr;
1360 for (i = 0; (Dwarf_Signed) i < lcount; i++) {
1361 if (dwarf_lineaddr(lbuf[i], &lineaddr, &de)) {
1362 warnx("dwarf_lineaddr: %s", dwarf_errmsg(de));
1363 continue;
1364 }
1365 if (dwarf_lineno(lbuf[i], &lineno, &de)) {
1366 warnx("dwarf_lineno: %s", dwarf_errmsg(de));
1367 continue;
1368 }
1369 if (dwarf_linesrc(lbuf[i], &sfile, &de)) {
1370 warnx("dwarf_linesrc: %s", dwarf_errmsg(de));
1371 continue;
1372 }
1373 if ((lie = malloc(sizeof(*lie))) == NULL) {
1374 warn("malloc");
1375 continue;
1376 }
1377 lie->addr = lineaddr;
1378 lie->line = lineno;
1379 lie->file = strdup(sfile);
1380 if (lie->file == NULL) {
1381 warn("strdup");
1382 free(lie);
1383 continue;
1384 }
1385 SLIST_INSERT_HEAD(line_info, lie, entries);
1386 }
1387
1388 line_attr:
1389 /* Retrieve line number information from DIEs. */
1390 search_line_attr(dbg, func_info, var_info, die, src_files, filecount);
1391 }
1392
1393 (void) dwarf_finish(dbg, &de);
1394
1395 process_sym:
1396
1397 p_data.list_num = get_sym(elf, &list_head, shnum, dynndx, strndx,
1398 type_table, (void *) sec_table, shnum);
1399
1400 if (p_data.list_num == 0)
1401 goto next_cmd;
1402
1403 p_data.headp = &list_head;
1404 p_data.sh_num = shnum;
1405 p_data.t_table = type_table;
1406 p_data.s_table = (void *) sec_table;
1407 p_data.filename = filename;
1408 p_data.objname = objname;
1409
1410 sym_list_print(&p_data, func_info, var_info, line_info);
1411
1412 next_cmd:
1413 if (nm_opts.debug_line) {
1414 if (func_info != NULL) {
1415 while (!SLIST_EMPTY(func_info)) {
1416 func = SLIST_FIRST(func_info);
1417 SLIST_REMOVE_HEAD(func_info, entries);
1418 free(func->file);
1419 free(func->name);
1420 free(func);
1421 }
1422 free(func_info);
1423 func_info = NULL;
1424 }
1425 if (var_info != NULL) {
1426 while (!SLIST_EMPTY(var_info)) {
1427 var = SLIST_FIRST(var_info);
1428 SLIST_REMOVE_HEAD(var_info, entries);
1429 free(var->file);
1430 free(var->name);
1431 free(var);
1432 }
1433 free(var_info);
1434 var_info = NULL;
1435 }
1436 if (line_info != NULL) {
1437 while (!SLIST_EMPTY(line_info)) {
1438 lie = SLIST_FIRST(line_info);
1439 SLIST_REMOVE_HEAD(line_info, entries);
1440 free(lie->file);
1441 free(lie);
1442 }
1443 free(line_info);
1444 line_info = NULL;
1445 }
1446 }
1447
1448 if (sec_table != NULL)
1449 for (i = 0; i < shnum; ++i)
1450 free(sec_table[i]);
1451 free(sec_table);
1452 free(type_table);
1453
1454 sym_list_dest(&list_head);
1455
1456 return (rtn);
1457
1458 #undef OBJNAME
1459 }
1460
1461 static int
read_object(const char * filename)1462 read_object(const char *filename)
1463 {
1464 Elf *elf, *arf;
1465 Elf_Cmd elf_cmd;
1466 Elf_Kind kind;
1467 int fd, rtn, e_err;
1468
1469 assert(filename != NULL && "filename is null");
1470
1471 if ((fd = open(filename, O_RDONLY)) == -1) {
1472 warn("'%s'", filename);
1473 return (1);
1474 }
1475
1476 elf_cmd = ELF_C_READ;
1477 if ((arf = elf_begin(fd, elf_cmd, (Elf *) NULL)) == NULL) {
1478 if ((e_err = elf_errno()) != 0)
1479 warnx("elf_begin error: %s", elf_errmsg(e_err));
1480 else
1481 warnx("elf_begin error");
1482 close(fd);
1483 return (1);
1484 }
1485
1486 assert(arf != NULL && "arf is null.");
1487
1488 rtn = 0;
1489 if ((kind = elf_kind(arf)) == ELF_K_NONE) {
1490 warnx("%s: File format not recognized", filename);
1491 elf_end(arf);
1492 close(fd);
1493 return (1);
1494 }
1495 if (kind == ELF_K_AR) {
1496 if (nm_opts.print_name == PRINT_NAME_MULTI &&
1497 nm_opts.elem_print_fn == sym_elem_print_all)
1498 printf("\n%s:\n", filename);
1499 if (nm_opts.print_armap == true)
1500 print_ar_index(fd, arf);
1501 }
1502
1503 while ((elf = elf_begin(fd, elf_cmd, arf)) != NULL) {
1504 rtn |= read_elf(elf, filename, kind);
1505
1506 /*
1507 * If file is not archive, elf_next return ELF_C_NULL and
1508 * stop the loop.
1509 */
1510 elf_cmd = elf_next(elf);
1511 elf_end(elf);
1512 }
1513
1514 elf_end(arf);
1515 close(fd);
1516
1517 return (rtn);
1518 }
1519
1520 static int
read_files(int argc,char ** argv)1521 read_files(int argc, char **argv)
1522 {
1523 int rtn = 0;
1524
1525 if (argc < 0 || argv == NULL)
1526 return (1);
1527
1528 if (argc == 0)
1529 rtn |= read_object(nm_info.def_filename);
1530 else {
1531 if (nm_opts.print_name == PRINT_NAME_NONE && argc > 1)
1532 nm_opts.print_name = PRINT_NAME_MULTI;
1533 while (argc > 0) {
1534 rtn |= read_object(*argv);
1535 --argc;
1536 ++argv;
1537 }
1538 }
1539
1540 return (rtn);
1541 }
1542
1543 static void
print_lineno(struct sym_entry * ep,struct func_info_head * func_info,struct var_info_head * var_info,struct line_info_head * line_info)1544 print_lineno(struct sym_entry *ep, struct func_info_head *func_info,
1545 struct var_info_head *var_info, struct line_info_head *line_info)
1546 {
1547 struct func_info_entry *func;
1548 struct var_info_entry *var;
1549 struct line_info_entry *lie;
1550
1551 /* For function symbol, search the function line information list. */
1552 if ((ep->sym->st_info & 0xf) == STT_FUNC && func_info != NULL) {
1553 SLIST_FOREACH(func, func_info, entries) {
1554 if (func->name != NULL &&
1555 !strcmp(ep->name, func->name) &&
1556 ep->sym->st_value >= func->lowpc &&
1557 ep->sym->st_value < func->highpc) {
1558 printf("\t%s:%" PRIu64, func->file, func->line);
1559 return;
1560 }
1561 }
1562 }
1563
1564 /* For variable symbol, search the variable line information list. */
1565 if ((ep->sym->st_info & 0xf) == STT_OBJECT && var_info != NULL) {
1566 SLIST_FOREACH(var, var_info, entries) {
1567 if (!strcmp(ep->name, var->name) &&
1568 ep->sym->st_value == var->addr) {
1569 printf("\t%s:%" PRIu64, var->file, var->line);
1570 return;
1571 }
1572 }
1573 }
1574
1575 /* Otherwise search line number information the .debug_line section. */
1576 if (line_info != NULL) {
1577 SLIST_FOREACH(lie, line_info, entries) {
1578 if (ep->sym->st_value == lie->addr) {
1579 printf("\t%s:%" PRIu64, lie->file, lie->line);
1580 return;
1581 }
1582 }
1583 }
1584 }
1585
1586 static void
set_opt_value_print_fn(enum radix t)1587 set_opt_value_print_fn(enum radix t)
1588 {
1589
1590 switch (t) {
1591 case RADIX_OCT:
1592 nm_opts.value_print_fn = &sym_value_oct_print;
1593 nm_opts.size_print_fn = &sym_size_oct_print;
1594
1595 break;
1596 case RADIX_DEC:
1597 nm_opts.value_print_fn = &sym_value_dec_print;
1598 nm_opts.size_print_fn = &sym_size_dec_print;
1599
1600 break;
1601 case RADIX_HEX:
1602 default :
1603 nm_opts.value_print_fn = &sym_value_hex_print;
1604 nm_opts.size_print_fn = &sym_size_hex_print;
1605 }
1606
1607 assert(nm_opts.value_print_fn != NULL &&
1608 "nm_opts.value_print_fn is null");
1609 }
1610
1611 static void
sym_elem_print_all(char type,const char * sec,const GElf_Sym * sym,const char * name)1612 sym_elem_print_all(char type, const char *sec, const GElf_Sym *sym,
1613 const char *name)
1614 {
1615
1616 if (sec == NULL || sym == NULL || name == NULL ||
1617 nm_opts.value_print_fn == NULL)
1618 return;
1619
1620 if (IS_UNDEF_SYM_TYPE(type)) {
1621 if (nm_opts.t == RADIX_HEX && nm_elfclass == ELFCLASS32)
1622 printf("%-8s", "");
1623 else
1624 printf("%-16s", "");
1625 } else {
1626 switch ((nm_opts.sort_fn == & cmp_size ? 2 : 0) +
1627 nm_opts.print_size) {
1628 case 3:
1629 if (sym->st_size != 0) {
1630 nm_opts.value_print_fn(sym);
1631 printf(" ");
1632 nm_opts.size_print_fn(sym);
1633 }
1634 break;
1635
1636 case 2:
1637 if (sym->st_size != 0)
1638 nm_opts.size_print_fn(sym);
1639 break;
1640
1641 case 1:
1642 nm_opts.value_print_fn(sym);
1643 if (sym->st_size != 0) {
1644 printf(" ");
1645 nm_opts.size_print_fn(sym);
1646 }
1647 break;
1648
1649 case 0:
1650 default:
1651 nm_opts.value_print_fn(sym);
1652 }
1653 }
1654
1655 printf(" %c ", type);
1656 PRINT_DEMANGLED_NAME("%s", name);
1657 }
1658
1659 static void
sym_elem_print_all_portable(char type,const char * sec,const GElf_Sym * sym,const char * name)1660 sym_elem_print_all_portable(char type, const char *sec, const GElf_Sym *sym,
1661 const char *name)
1662 {
1663
1664 if (sec == NULL || sym == NULL || name == NULL ||
1665 nm_opts.value_print_fn == NULL)
1666 return;
1667
1668 PRINT_DEMANGLED_NAME("%s", name);
1669 printf(" %c ", type);
1670 if (!IS_UNDEF_SYM_TYPE(type)) {
1671 nm_opts.value_print_fn(sym);
1672 printf(" ");
1673 if (sym->st_size != 0)
1674 nm_opts.size_print_fn(sym);
1675 } else
1676 printf(" ");
1677 }
1678
1679 static void
sym_elem_print_all_sysv(char type,const char * sec,const GElf_Sym * sym,const char * name)1680 sym_elem_print_all_sysv(char type, const char *sec, const GElf_Sym *sym,
1681 const char *name)
1682 {
1683
1684 if (sec == NULL || sym == NULL || name == NULL ||
1685 nm_opts.value_print_fn == NULL)
1686 return;
1687
1688 PRINT_DEMANGLED_NAME("%-20s|", name);
1689 if (IS_UNDEF_SYM_TYPE(type))
1690 printf(" ");
1691 else
1692 nm_opts.value_print_fn(sym);
1693
1694 printf("| %c |", type);
1695
1696 switch (sym->st_info & 0xf) {
1697 case STT_OBJECT:
1698 printf("%18s|", "OBJECT");
1699 break;
1700
1701 case STT_FUNC:
1702 printf("%18s|", "FUNC");
1703 break;
1704
1705 case STT_SECTION:
1706 printf("%18s|", "SECTION");
1707 break;
1708
1709 case STT_FILE:
1710 printf("%18s|", "FILE");
1711 break;
1712
1713 case STT_LOPROC:
1714 printf("%18s|", "LOPROC");
1715 break;
1716
1717 case STT_HIPROC:
1718 printf("%18s|", "HIPROC");
1719 break;
1720
1721 case STT_NOTYPE:
1722 default:
1723 printf("%18s|", "NOTYPE");
1724 }
1725
1726 if (sym->st_size != 0)
1727 nm_opts.size_print_fn(sym);
1728 else
1729 printf(" ");
1730
1731 printf("| |%s", sec);
1732 }
1733
1734 static int
sym_elem_def(char type,const GElf_Sym * sym,const char * name)1735 sym_elem_def(char type, const GElf_Sym *sym, const char *name)
1736 {
1737
1738 assert(IS_SYM_TYPE((unsigned char) type));
1739
1740 UNUSED(sym);
1741 UNUSED(name);
1742
1743 return (!IS_UNDEF_SYM_TYPE((unsigned char) type));
1744 }
1745
1746 static int
sym_elem_global(char type,const GElf_Sym * sym,const char * name)1747 sym_elem_global(char type, const GElf_Sym *sym, const char *name)
1748 {
1749
1750 assert(IS_SYM_TYPE((unsigned char) type));
1751
1752 UNUSED(sym);
1753 UNUSED(name);
1754
1755 /* weak symbols resemble global. */
1756 return (isupper((unsigned char) type) || type == 'w');
1757 }
1758
1759 static int
sym_elem_global_static(char type,const GElf_Sym * sym,const char * name)1760 sym_elem_global_static(char type, const GElf_Sym *sym, const char *name)
1761 {
1762 unsigned char info;
1763
1764 assert(sym != NULL);
1765
1766 UNUSED(type);
1767 UNUSED(name);
1768
1769 info = sym->st_info >> 4;
1770
1771 return (info == STB_LOCAL ||
1772 info == STB_GLOBAL ||
1773 info == STB_WEAK);
1774 }
1775
1776 static int
sym_elem_nondebug(char type,const GElf_Sym * sym,const char * name)1777 sym_elem_nondebug(char type, const GElf_Sym *sym, const char *name)
1778 {
1779
1780 assert(sym != NULL);
1781
1782 UNUSED(type);
1783 UNUSED(name);
1784
1785 if (sym->st_value == 0 && (sym->st_info & 0xf) == STT_FILE)
1786 return (0);
1787 if (sym->st_name == 0)
1788 return (0);
1789
1790 return (1);
1791 }
1792
1793 static int
sym_elem_nonzero_size(char type,const GElf_Sym * sym,const char * name)1794 sym_elem_nonzero_size(char type, const GElf_Sym *sym, const char *name)
1795 {
1796
1797 assert(sym != NULL);
1798
1799 UNUSED(type);
1800 UNUSED(name);
1801
1802 return (sym->st_size > 0);
1803 }
1804
1805 static int
sym_elem_undef(char type,const GElf_Sym * sym,const char * name)1806 sym_elem_undef(char type, const GElf_Sym *sym, const char *name)
1807 {
1808
1809 assert(IS_SYM_TYPE((unsigned char) type));
1810
1811 UNUSED(sym);
1812 UNUSED(name);
1813
1814 return (IS_UNDEF_SYM_TYPE((unsigned char) type));
1815 }
1816
1817 static void
sym_list_dest(struct sym_head * headp)1818 sym_list_dest(struct sym_head *headp)
1819 {
1820 struct sym_entry *ep, *ep_n;
1821
1822 if (headp == NULL)
1823 return;
1824
1825 ep = STAILQ_FIRST(headp);
1826 while (ep != NULL) {
1827 ep_n = STAILQ_NEXT(ep, sym_entries);
1828 free(ep->sym);
1829 free(ep->name);
1830 free(ep);
1831 ep = ep_n;
1832 }
1833 }
1834
1835 static int
sym_list_insert(struct sym_head * headp,const char * name,const GElf_Sym * sym)1836 sym_list_insert(struct sym_head *headp, const char *name, const GElf_Sym *sym)
1837 {
1838 struct sym_entry *e;
1839
1840 if (headp == NULL || name == NULL || sym == NULL)
1841 return (0);
1842 if ((e = malloc(sizeof(struct sym_entry))) == NULL) {
1843 warn("malloc");
1844 return (0);
1845 }
1846 if ((e->name = strdup(name)) == NULL) {
1847 warn("strdup");
1848 free(e);
1849 return (0);
1850 }
1851 if ((e->sym = malloc(sizeof(GElf_Sym))) == NULL) {
1852 warn("malloc");
1853 free(e->name);
1854 free(e);
1855 return (0);
1856 }
1857
1858 memcpy(e->sym, sym, sizeof(GElf_Sym));
1859
1860 /* Display size instead of value for common symbol. */
1861 if (sym->st_shndx == SHN_COMMON)
1862 e->sym->st_value = sym->st_size;
1863
1864 STAILQ_INSERT_TAIL(headp, e, sym_entries);
1865
1866 return (1);
1867 }
1868
1869 /* If file has not .debug_info, line_info will be NULL */
1870 static void
sym_list_print(struct sym_print_data * p,struct func_info_head * func_info,struct var_info_head * var_info,struct line_info_head * line_info)1871 sym_list_print(struct sym_print_data *p, struct func_info_head *func_info,
1872 struct var_info_head *var_info, struct line_info_head *line_info)
1873 {
1874 struct sym_entry *e_v;
1875 size_t si;
1876 int i;
1877
1878 if (p == NULL || CHECK_SYM_PRINT_DATA(p))
1879 return;
1880 if ((e_v = sym_list_sort(p)) == NULL)
1881 return;
1882 if (nm_opts.sort_reverse == false)
1883 for (si = 0; si != p->list_num; ++si)
1884 sym_list_print_each(&e_v[si], p, func_info, var_info,
1885 line_info);
1886 else
1887 for (i = p->list_num - 1; i != -1; --i)
1888 sym_list_print_each(&e_v[i], p, func_info, var_info,
1889 line_info);
1890
1891 free(e_v);
1892 }
1893
1894 /* If file has not .debug_info, line_info will be NULL */
1895 static void
sym_list_print_each(struct sym_entry * ep,struct sym_print_data * p,struct func_info_head * func_info,struct var_info_head * var_info,struct line_info_head * line_info)1896 sym_list_print_each(struct sym_entry *ep, struct sym_print_data *p,
1897 struct func_info_head *func_info, struct var_info_head *var_info,
1898 struct line_info_head *line_info)
1899 {
1900 const char *sec;
1901 char type;
1902
1903 if (ep == NULL || CHECK_SYM_PRINT_DATA(p))
1904 return;
1905
1906 assert(ep->name != NULL);
1907 assert(ep->sym != NULL);
1908
1909 type = get_sym_type(ep->sym, p->t_table);
1910
1911 if (nm_opts.print_name == PRINT_NAME_FULL) {
1912 printf("%s", p->filename);
1913 if (nm_opts.elem_print_fn == &sym_elem_print_all_portable) {
1914 if (p->objname != NULL)
1915 printf("[%s]", p->objname);
1916 printf(": ");
1917 } else {
1918 if (p->objname != NULL)
1919 printf(":%s", p->objname);
1920 printf(":");
1921 }
1922 }
1923
1924 switch (ep->sym->st_shndx) {
1925 case SHN_LOPROC:
1926 /* LOPROC or LORESERVE */
1927 sec = "*LOPROC*";
1928 break;
1929 case SHN_HIPROC:
1930 sec = "*HIPROC*";
1931 break;
1932 case SHN_LOOS:
1933 sec = "*LOOS*";
1934 break;
1935 case SHN_HIOS:
1936 sec = "*HIOS*";
1937 break;
1938 case SHN_ABS:
1939 sec = "*ABS*";
1940 break;
1941 case SHN_COMMON:
1942 sec = "*COM*";
1943 break;
1944 case SHN_HIRESERVE:
1945 /* HIRESERVE or XINDEX */
1946 sec = "*HIRESERVE*";
1947 break;
1948 default:
1949 if (ep->sym->st_shndx > p->sh_num)
1950 return;
1951 sec = p->s_table[ep->sym->st_shndx];
1952 break;
1953 }
1954
1955 nm_opts.elem_print_fn(type, sec, ep->sym, ep->name);
1956
1957 if (nm_opts.debug_line == true && !IS_UNDEF_SYM_TYPE(type))
1958 print_lineno(ep, func_info, var_info, line_info);
1959
1960 printf("\n");
1961 }
1962
1963 static struct sym_entry *
sym_list_sort(struct sym_print_data * p)1964 sym_list_sort(struct sym_print_data *p)
1965 {
1966 struct sym_entry *ep, *e_v;
1967 int idx;
1968
1969 if (p == NULL || CHECK_SYM_PRINT_DATA(p))
1970 return (NULL);
1971
1972 if ((e_v = malloc(sizeof(struct sym_entry) * p->list_num)) == NULL) {
1973 warn("malloc");
1974 return (NULL);
1975 }
1976
1977 idx = 0;
1978 STAILQ_FOREACH(ep, p->headp, sym_entries) {
1979 if (ep->name != NULL && ep->sym != NULL) {
1980 e_v[idx].name = ep->name;
1981 e_v[idx].sym = ep->sym;
1982 ++idx;
1983 }
1984 }
1985
1986 assert((size_t)idx == p->list_num);
1987
1988 if (nm_opts.sort_fn != &cmp_none) {
1989 nm_print_data = p;
1990 assert(nm_print_data != NULL);
1991 qsort(e_v, p->list_num, sizeof(struct sym_entry),
1992 nm_opts.sort_fn);
1993 }
1994
1995 return (e_v);
1996 }
1997
1998 static void
sym_size_oct_print(const GElf_Sym * sym)1999 sym_size_oct_print(const GElf_Sym *sym)
2000 {
2001
2002 assert(sym != NULL && "sym is null");
2003 printf("%016" PRIo64, sym->st_size);
2004 }
2005
2006 static void
sym_size_hex_print(const GElf_Sym * sym)2007 sym_size_hex_print(const GElf_Sym *sym)
2008 {
2009
2010 assert(sym != NULL && "sym is null");
2011 if (nm_elfclass == ELFCLASS32)
2012 printf("%08" PRIx64, sym->st_size);
2013 else
2014 printf("%016" PRIx64, sym->st_size);
2015 }
2016
2017 static void
sym_size_dec_print(const GElf_Sym * sym)2018 sym_size_dec_print(const GElf_Sym *sym)
2019 {
2020
2021 assert(sym != NULL && "sym is null");
2022 printf("%016" PRId64, sym->st_size);
2023 }
2024
2025 static void
sym_value_oct_print(const GElf_Sym * sym)2026 sym_value_oct_print(const GElf_Sym *sym)
2027 {
2028
2029 assert(sym != NULL && "sym is null");
2030 printf("%016" PRIo64, sym->st_value);
2031 }
2032
2033 static void
sym_value_hex_print(const GElf_Sym * sym)2034 sym_value_hex_print(const GElf_Sym *sym)
2035 {
2036
2037 assert(sym != NULL && "sym is null");
2038 if (nm_elfclass == ELFCLASS32)
2039 printf("%08" PRIx64, sym->st_value);
2040 else
2041 printf("%016" PRIx64, sym->st_value);
2042 }
2043
2044 static void
sym_value_dec_print(const GElf_Sym * sym)2045 sym_value_dec_print(const GElf_Sym *sym)
2046 {
2047
2048 assert(sym != NULL && "sym is null");
2049 printf("%016" PRId64, sym->st_value);
2050 }
2051
2052 static void
usage(int exitcode)2053 usage(int exitcode)
2054 {
2055
2056 printf("Usage: %s [options] file ...\
2057 \n Display symbolic information in file.\n\
2058 \n Options: \
2059 \n -A, --print-file-name Write the full pathname or library name of an\
2060 \n object on each line.\
2061 \n -a, --debug-syms Display all symbols include debugger-only\
2062 \n symbols.", nm_info.name);
2063 printf("\
2064 \n -B Equivalent to specifying \"--format=bsd\".\
2065 \n -C, --demangle[=style] Decode low-level symbol names.\
2066 \n --no-demangle Do not demangle low-level symbol names.\
2067 \n -D, --dynamic Display only dynamic symbols.\
2068 \n -e Display only global and static symbols.");
2069 printf("\
2070 \n -f Produce full output (default).\
2071 \n --format=format Display output in specific format. Allowed\
2072 \n formats are: \"bsd\", \"posix\" and \"sysv\".\
2073 \n -g, --extern-only Display only global symbol information.\
2074 \n -h, --help Show this help message.\
2075 \n -l, --line-numbers Display filename and linenumber using\
2076 \n debugging information.\
2077 \n -n, --numeric-sort Sort symbols numerically by value.");
2078 printf("\
2079 \n -o Write numeric values in octal. Equivalent to\
2080 \n specifying \"-t o\".\
2081 \n -p, --no-sort Do not sort symbols.\
2082 \n -P Write information in a portable output format.\
2083 \n Equivalent to specifying \"--format=posix\".\
2084 \n -r, --reverse-sort Reverse the order of the sort.\
2085 \n -S, --print-size Print symbol sizes instead values.\
2086 \n -s, --print-armap Include an index of archive members.\
2087 \n --size-sort Sort symbols by size.");
2088 printf("\
2089 \n -t, --radix=format Write each numeric value in the specified\
2090 \n format:\
2091 \n d In decimal,\
2092 \n o In octal,\
2093 \n x In hexadecimal.");
2094 printf("\
2095 \n -u, --undefined-only Display only undefined symbols.\
2096 \n --defined-only Display only defined symbols.\
2097 \n -V, --version Show the version identifier for %s.\
2098 \n -v Sort output by value.\
2099 \n -x Write numeric values in hexadecimal.\
2100 \n Equivalent to specifying \"-t x\".",
2101 nm_info.name);
2102 printf("\n\
2103 \n The default options are: output in bsd format, use a hexadecimal radix,\
2104 \n sort by symbol name, do not demangle names.\n");
2105
2106 exit(exitcode);
2107 }
2108
2109 /*
2110 * Display symbolic information in file.
2111 * Return 0 at success, >0 at failed.
2112 */
2113 int
main(int argc,char ** argv)2114 main(int argc, char **argv)
2115 {
2116 int rtn;
2117
2118 global_init();
2119 get_opt(&argc, &argv);
2120 rtn = read_files(argc, argv);
2121 global_dest();
2122
2123 exit(rtn);
2124 }
2125