• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Print symbol information from ELF file in human-readable form.
2    Copyright (C) 2000,2001,2002,2003,2004,2005,2006,2007,2008 Red Hat, Inc.
3    This file is part of Red Hat elfutils.
4    Written by Ulrich Drepper <drepper@redhat.com>, 2000.
5 
6    Red Hat elfutils is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by the
8    Free Software Foundation; version 2 of the License.
9 
10    Red Hat elfutils is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License along
16    with Red Hat elfutils; if not, write to the Free Software Foundation,
17    Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
18 
19    Red Hat elfutils is an included package of the Open Invention Network.
20    An included package of the Open Invention Network is a package for which
21    Open Invention Network licensees cross-license their patents.  No patent
22    license is granted, either expressly or impliedly, by designation as an
23    included package.  Should you wish to participate in the Open Invention
24    Network licensing program, please visit www.openinventionnetwork.com
25    <http://www.openinventionnetwork.com>.  */
26 
27 #ifdef HAVE_CONFIG_H
28 # include <config.h>
29 #endif
30 
31 #include <ar.h>
32 #include <argp.h>
33 #include <assert.h>
34 #include <ctype.h>
35 #include <dwarf.h>
36 #include <errno.h>
37 #include <error.h>
38 #include <fcntl.h>
39 #include <gelf.h>
40 #include <inttypes.h>
41 #include <libdw.h>
42 #include <libintl.h>
43 #include <locale.h>
44 #include <mcheck.h>
45 #include <obstack.h>
46 #include <search.h>
47 #include <stdbool.h>
48 #include <stdio.h>
49 #include <stdio_ext.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53 #include <sys/param.h>
54 
55 #include <system.h>
56 #include "../libebl/libeblP.h"
57 
58 
59 /* Name and version of program.  */
60 static void print_version (FILE *stream, struct argp_state *state);
61 void (*argp_program_version_hook) (FILE *, struct argp_state *) = print_version;
62 
63 /* Bug report address.  */
64 const char *argp_program_bug_address = PACKAGE_BUGREPORT;
65 
66 
67 /* Values for the parameters which have no short form.  */
68 #define OPT_DEFINED	0x100
69 #define OPT_MARK_WEAK	0x101
70 
71 /* Definitions of arguments for argp functions.  */
72 static const struct argp_option options[] =
73 {
74   { NULL, 0, NULL, 0, N_("Output selection:"), 0 },
75   { "debug-syms", 'a', NULL, 0, N_("Display debugger-only symbols"), 0 },
76   { "defined-only", OPT_DEFINED, NULL, 0, N_("Display only defined symbols"),
77     0 },
78   { "dynamic", 'D', NULL, 0,
79     N_("Display dynamic symbols instead of normal symbols"), 0 },
80   { "extern-only", 'g', NULL, 0, N_("Display only external symbols"), 0 },
81   { "undefined-only", 'u', NULL, 0, N_("Display only undefined symbols"), 0 },
82   { "print-armap", 's', NULL, 0,
83     N_("Include index for symbols from archive members"), 0 },
84 
85   { NULL, 0, NULL, 0, N_("Output format:"), 0 },
86   { "print-file-name", 'A', NULL, 0,
87     N_("Print name of the input file before every symbol"), 0 },
88   { NULL, 'o', NULL, OPTION_HIDDEN, "Same as -A", 0 },
89   { "format", 'f', "FORMAT", 0,
90     N_("Use the output format FORMAT.  FORMAT can be `bsd', `sysv' or `posix'.  The default is `sysv'"),
91     0 },
92   { NULL, 'B', NULL, 0, N_("Same as --format=bsd"), 0 },
93   { "portability", 'P', NULL, 0, N_("Same as --format=posix"), 0 },
94   { "radix", 't', "RADIX", 0, N_("Use RADIX for printing symbol values"), 0 },
95   { "mark-weak", OPT_MARK_WEAK, NULL, 0, N_("Mark weak symbols"), 0 },
96   { "print-size", 'S', NULL, 0, N_("Print size of defined symbols"), 0 },
97 
98   { NULL, 0, NULL, 0, N_("Output options:"), 0 },
99   { "numeric-sort", 'n', NULL, 0, N_("Sort symbols numerically by address"),
100     0 },
101   { "no-sort", 'p', NULL, 0, N_("Do not sort the symbols"), 0 },
102   { "reverse-sort", 'r', NULL, 0, N_("Reverse the sense of the sort"), 0 },
103   { NULL, 0, NULL, 0, N_("Miscellaneous:"), 0 },
104   { NULL, 0, NULL, 0, NULL, 0 }
105 };
106 
107 /* Short description of program.  */
108 static const char doc[] = N_("List symbols from FILEs (a.out by default).");
109 
110 /* Strings for arguments in help texts.  */
111 static const char args_doc[] = N_("[FILE...]");
112 
113 /* Prototype for option handler.  */
114 static error_t parse_opt (int key, char *arg, struct argp_state *state);
115 
116 /* Data structure to communicate with argp functions.  */
117 static struct argp argp =
118 {
119   options, parse_opt, args_doc, doc, NULL, NULL, NULL
120 };
121 
122 
123 /* Print symbols in file named FNAME.  */
124 static int process_file (const char *fname, bool more_than_one);
125 
126 /* Handle content of archive.  */
127 static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
128 		      const char *suffix);
129 
130 /* Handle ELF file.  */
131 static int handle_elf (Elf *elf, const char *prefix, const char *fname,
132 		       const char *suffix);
133 
134 
135 #define INTERNAL_ERROR(fname) \
136   error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"),      \
137 	 fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1))
138 
139 
140 /* Internal representation of symbols.  */
141 typedef struct GElf_SymX
142 {
143   GElf_Sym sym;
144   Elf32_Word xndx;
145   char *where;
146 } GElf_SymX;
147 
148 
149 /* User-selectable options.  */
150 
151 /* The selected output format.  */
152 static enum
153 {
154   format_sysv = 0,
155   format_bsd,
156   format_posix
157 } format;
158 
159 /* Print defined, undefined, or both?  */
160 static bool hide_undefined;
161 static bool hide_defined;
162 
163 /* Print local symbols also?  */
164 static bool hide_local;
165 
166 /* Nonzero if full filename should precede every symbol.  */
167 static bool print_file_name;
168 
169 /* If true print size of defined symbols in BSD format.  */
170 static bool print_size;
171 
172 /* If true print archive index.  */
173 static bool print_armap;
174 
175 /* If true reverse sorting.  */
176 static bool reverse_sort;
177 
178 /* Type of the section we are printing.  */
179 static GElf_Word symsec_type = SHT_SYMTAB;
180 
181 /* Sorting selection.  */
182 static enum
183 {
184   sort_name = 0,
185   sort_numeric,
186   sort_nosort
187 } sort;
188 
189 /* Radix for printed numbers.  */
190 static enum
191 {
192   radix_hex = 0,
193   radix_decimal,
194   radix_octal
195 } radix;
196 
197 /* If nonzero weak symbols are distinguished from global symbols by adding
198    a `*' after the identifying letter for the symbol class and type.  */
199 static bool mark_weak;
200 
201 
202 int
main(int argc,char * argv[])203 main (int argc, char *argv[])
204 {
205   int remaining;
206   int result = 0;
207 
208   /* Make memory leak detection possible.  */
209   mtrace ();
210 
211   /* We use no threads here which can interfere with handling a stream.  */
212   (void) __fsetlocking (stdin, FSETLOCKING_BYCALLER);
213   (void) __fsetlocking (stdout, FSETLOCKING_BYCALLER);
214   (void) __fsetlocking (stderr, FSETLOCKING_BYCALLER);
215 
216   /* Set locale.  */
217   (void) setlocale (LC_ALL, "");
218 
219   /* Make sure the message catalog can be found.  */
220   (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
221 
222   /* Initialize the message catalog.  */
223   (void) textdomain (PACKAGE_TARNAME);
224 
225   /* Parse and process arguments.  */
226   (void) argp_parse (&argp, argc, argv, 0, &remaining, NULL);
227 
228   /* Tell the library which version we are expecting.  */
229   (void) elf_version (EV_CURRENT);
230 
231   if (remaining == argc)
232     /* The user didn't specify a name so we use a.out.  */
233     result = process_file ("a.out", false);
234   else
235     {
236       /* Process all the remaining files.  */
237       const bool more_than_one = remaining + 1 < argc;
238 
239       do
240 	result |= process_file (argv[remaining], more_than_one);
241       while (++remaining < argc);
242     }
243 
244   return result;
245 }
246 
247 
248 /* Print the version information.  */
249 static void
print_version(FILE * stream,struct argp_state * state)250 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
251 {
252   fprintf (stream, "nm (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
253   fprintf (stream, gettext ("\
254 Copyright (C) %s Red Hat, Inc.\n\
255 This is free software; see the source for copying conditions.  There is NO\n\
256 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
257 "), "2008");
258   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
259 }
260 
261 
262 /* Handle program arguments.  */
263 static error_t
parse_opt(int key,char * arg,struct argp_state * state)264 parse_opt (int key, char *arg,
265 	   struct argp_state *state __attribute__ ((unused)))
266 {
267   switch (key)
268     {
269     case 'a':
270       /* XXX */
271       break;
272 
273     case 'f':
274       if (strcmp (arg, "bsd") == 0)
275 	format = format_bsd;
276       else if (strcmp (arg, "posix") == 0)
277 	format = format_posix;
278       else
279 	/* Be bug compatible.  The BFD implementation also defaulted to
280 	   using the SysV format if nothing else matches.  */
281 	format = format_sysv;
282       break;
283 
284     case 'g':
285       hide_local = true;
286       break;
287 
288     case 'n':
289       sort = sort_numeric;
290       break;
291 
292     case 'p':
293       sort = sort_nosort;
294       break;
295 
296     case 't':
297       if (strcmp (arg, "10") == 0 || strcmp (arg, "d") == 0)
298 	radix = radix_decimal;
299       else if (strcmp (arg, "8") == 0 || strcmp (arg, "o") == 0)
300 	radix = radix_octal;
301       else
302 	radix = radix_hex;
303       break;
304 
305     case 'u':
306       hide_undefined = false;
307       hide_defined = true;
308       break;
309 
310     case 'A':
311     case 'o':
312       print_file_name = true;
313       break;
314 
315     case 'B':
316       format = format_bsd;
317       break;
318 
319     case 'D':
320       symsec_type = SHT_DYNSYM;
321       break;
322 
323     case 'P':
324       format = format_posix;
325       break;
326 
327     case OPT_DEFINED:
328       hide_undefined = true;
329       hide_defined = false;
330       break;
331 
332     case OPT_MARK_WEAK:
333       mark_weak = true;
334       break;
335 
336     case 'S':
337       print_size = true;
338       break;
339 
340     case 's':
341       print_armap = true;
342       break;
343 
344     case 'r':
345       reverse_sort = true;
346       break;
347 
348     default:
349       return ARGP_ERR_UNKNOWN;
350     }
351   return 0;
352 }
353 
354 
355 /* Open the file and determine the type.  */
356 static int
process_file(const char * fname,bool more_than_one)357 process_file (const char *fname, bool more_than_one)
358 {
359   /* Open the file.  */
360   int fd = open (fname, O_RDONLY);
361   if (fd == -1)
362     {
363       error (0, errno, gettext ("cannot open '%s'"), fname);
364       return 1;
365     }
366 
367   /* Now get the ELF descriptor.  */
368   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
369   if (elf != NULL)
370     {
371       if (elf_kind (elf) == ELF_K_ELF)
372 	{
373 	  int result = handle_elf (elf, more_than_one ? "" : NULL,
374 				   fname, NULL);
375 
376 	  if (elf_end (elf) != 0)
377 	    INTERNAL_ERROR (fname);
378 
379 	  if (close (fd) != 0)
380 	    error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
381 
382 	  return result;
383 	}
384       else if (elf_kind (elf) == ELF_K_AR)
385 	{
386 	  int result = handle_ar (fd, elf, NULL, fname, NULL);
387 
388 	  if (elf_end (elf) != 0)
389 	    INTERNAL_ERROR (fname);
390 
391 	  if (close (fd) != 0)
392 	    error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
393 
394 	  return result;
395 	}
396 
397       /* We cannot handle this type.  Close the descriptor anyway.  */
398       if (elf_end (elf) != 0)
399 	INTERNAL_ERROR (fname);
400     }
401 
402   error (0, 0, gettext ("%s: File format not recognized"), fname);
403 
404   return 1;
405 }
406 
407 
408 static int
handle_ar(int fd,Elf * elf,const char * prefix,const char * fname,const char * suffix)409 handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
410 	   const char *suffix)
411 {
412   size_t fname_len = strlen (fname) + 1;
413   size_t prefix_len = prefix != NULL ? strlen (prefix) : 0;
414   char new_prefix[prefix_len + fname_len + 2];
415   size_t suffix_len = suffix != NULL ? strlen (suffix) : 0;
416   char new_suffix[suffix_len + 2];
417   Elf *subelf;
418   Elf_Cmd cmd = ELF_C_READ_MMAP;
419   int result = 0;
420 
421   char *cp = new_prefix;
422   if (prefix != NULL)
423     cp = stpcpy (cp, prefix);
424   cp = stpcpy (cp, fname);
425   stpcpy (cp, "[");
426 
427   cp = new_suffix;
428   if (suffix != NULL)
429     cp = stpcpy (cp, suffix);
430   stpcpy (cp, "]");
431 
432   /* First print the archive index if this is wanted.  */
433   if (print_armap)
434     {
435       Elf_Arsym *arsym = elf_getarsym (elf, NULL);
436 
437       if (arsym != NULL)
438 	{
439 	  Elf_Arhdr *arhdr = NULL;
440 	  size_t arhdr_off = 0;	/* Note: 0 is no valid offset.  */
441 
442 	  puts (gettext("\nArchive index:"));
443 
444 	  while (arsym->as_off != 0)
445 	    {
446 	      if (arhdr_off != arsym->as_off
447 		  && (elf_rand (elf, arsym->as_off) != arsym->as_off
448 		      || (subelf = elf_begin (fd, cmd, elf)) == NULL
449 		      || (arhdr = elf_getarhdr (subelf)) == NULL))
450 		{
451 		  error (0, 0, gettext ("invalid offset %zu for symbol %s"),
452 			 arsym->as_off, arsym->as_name);
453 		  continue;
454 		}
455 
456 	      printf (gettext ("%s in %s\n"), arsym->as_name, arhdr->ar_name);
457 
458 	      ++arsym;
459 	    }
460 
461 	  if (elf_rand (elf, SARMAG) != SARMAG)
462 	    {
463 	      error (0, 0,
464 		     gettext ("cannot reset archive offset to beginning"));
465 	      return 1;
466 	    }
467 	}
468     }
469 
470   /* Process all the files contained in the archive.  */
471   while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
472     {
473       /* The the header for this element.  */
474       Elf_Arhdr *arhdr = elf_getarhdr (subelf);
475 
476       /* Skip over the index entries.  */
477       if (strcmp (arhdr->ar_name, "/") != 0
478 	  && strcmp (arhdr->ar_name, "//") != 0)
479 	{
480 	  if (elf_kind (subelf) == ELF_K_ELF)
481 	    result |= handle_elf (subelf, new_prefix, arhdr->ar_name,
482 				  new_suffix);
483 	  else if (elf_kind (subelf) == ELF_K_AR)
484 	    result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name,
485 				 new_suffix);
486 	  else
487 	    {
488 	      error (0, 0, gettext ("%s%s%s: file format not recognized"),
489 		     new_prefix, arhdr->ar_name, new_suffix);
490 	      result = 1;
491 	    }
492 	}
493 
494       /* Get next archive element.  */
495       cmd = elf_next (subelf);
496       if (elf_end (subelf) != 0)
497 	INTERNAL_ERROR (fname);
498     }
499 
500   return result;
501 }
502 
503 
504 /* Mapping of radix and binary class to length.  */
505 static const int length_map[2][3] =
506 {
507   [ELFCLASS32 - 1] =
508   {
509     [radix_hex] = 8,
510     [radix_decimal] = 10,
511     [radix_octal] = 11
512   },
513   [ELFCLASS64 - 1] =
514   {
515     [radix_hex] = 16,
516     [radix_decimal] = 20,
517     [radix_octal] = 22
518   }
519 };
520 
521 
522 static int
global_compare(const void * p1,const void * p2)523 global_compare (const void *p1, const void *p2)
524 {
525   const Dwarf_Global *g1 = (const Dwarf_Global *) p1;
526   const Dwarf_Global *g2 = (const Dwarf_Global *) p2;
527 
528   return strcmp (g1->name, g2->name);
529 }
530 
531 
532 static void *global_root;
533 
534 
535 static int
get_global(Dwarf * dbg,Dwarf_Global * global,void * arg)536 get_global (Dwarf *dbg __attribute__ ((unused)), Dwarf_Global *global,
537 	    void *arg __attribute__ ((unused)))
538 {
539   tsearch (memcpy (xmalloc (sizeof (Dwarf_Global)), global,
540 		   sizeof (Dwarf_Global)),
541 	   &global_root, global_compare);
542 
543   return DWARF_CB_OK;
544 }
545 
546 
547 struct local_name
548 {
549   const char *name;
550   const char *file;
551   Dwarf_Word lineno;
552   Dwarf_Addr lowpc;
553   Dwarf_Addr highpc;
554 };
555 
556 
557 static int
local_compare(const void * p1,const void * p2)558 local_compare (const void *p1, const void *p2)
559 {
560   struct local_name *g1 = (struct local_name *) p1;
561   struct local_name *g2 = (struct local_name *) p2;
562   int result;
563 
564   result = strcmp (g1->name, g2->name);
565   if (result == 0)
566     {
567       if (g1->lowpc <= g2->lowpc && g1->highpc >= g2->highpc)
568 	{
569 	  /* g2 is contained in g1.  Update the data.  */
570 	  g2->lowpc = g1->lowpc;
571 	  g2->highpc = g1->highpc;
572 	  result = 0;
573 	}
574       else if (g2->lowpc <= g1->lowpc && g2->highpc >= g1->highpc)
575 	{
576 	  /* g1 is contained in g2.  Update the data.  */
577 	  g1->lowpc = g2->lowpc;
578 	  g1->highpc = g2->highpc;
579 	  result = 0;
580 	}
581       else
582 	result = g1->lowpc < g2->lowpc ? -1 : 1;
583     }
584 
585   return result;
586 }
587 
588 
589 static int
get_var_range(Dwarf_Die * die,Dwarf_Word * lowpc,Dwarf_Word * highpc)590 get_var_range (Dwarf_Die *die, Dwarf_Word *lowpc, Dwarf_Word *highpc)
591 {
592   Dwarf_Attribute locattr_mem;
593   Dwarf_Attribute *locattr = dwarf_attr (die, DW_AT_location, &locattr_mem);
594   if  (locattr == NULL)
595     return 1;
596 
597   Dwarf_Op *loc;
598   size_t nloc;
599   if (dwarf_getlocation (locattr, &loc, &nloc) != 0)
600     return 1;
601 
602   /* Interpret the location expressions.  */
603   // XXX For now just the simple one:
604   if (nloc == 1 && loc[0].atom == DW_OP_addr)
605     {
606       *lowpc = *highpc = loc[0].number;
607       return 0;
608     }
609 
610   return 1;
611 }
612 
613 
614 
615 static void *local_root;
616 
617 
618 static void
get_local_names(Dwarf * dbg)619 get_local_names (Dwarf *dbg)
620 {
621   Dwarf_Off offset = 0;
622   Dwarf_Off old_offset;
623   size_t hsize;
624 
625   while (dwarf_nextcu (dbg, old_offset = offset, &offset, &hsize, NULL, NULL,
626 		       NULL) == 0)
627     {
628       Dwarf_Die cudie_mem;
629       Dwarf_Die *cudie = dwarf_offdie (dbg, old_offset + hsize, &cudie_mem);
630 
631       /* If we cannot get the CU DIE there is no need to go on with
632 	 this CU.  */
633       if (cudie == NULL)
634 	continue;
635       /* This better be a CU DIE.  */
636       if (dwarf_tag (cudie) != DW_TAG_compile_unit)
637 	continue;
638 
639       /* Get the line information.  */
640       Dwarf_Files *files;
641       size_t nfiles;
642       if (dwarf_getsrcfiles (cudie, &files, &nfiles) != 0)
643 	continue;
644 
645       Dwarf_Die die_mem;
646       Dwarf_Die *die = &die_mem;
647       if (dwarf_child (cudie, die) == 0)
648 	/* Iterate over all immediate children of the CU DIE.  */
649 	do
650 	  {
651 	    int tag = dwarf_tag (die);
652 	    if (tag != DW_TAG_subprogram && tag != DW_TAG_variable)
653 	      continue;
654 
655 	    /* We are interested in five attributes: name, decl_file,
656 	       decl_line, low_pc, and high_pc.  */
657 	    Dwarf_Attribute attr_mem;
658 	    Dwarf_Attribute *attr = dwarf_attr (die, DW_AT_name, &attr_mem);
659 	    const char *name = dwarf_formstring (attr);
660 	    if (name == NULL)
661 	      continue;
662 
663 	    Dwarf_Word fileidx;
664 	    attr = dwarf_attr (die, DW_AT_decl_file, &attr_mem);
665 	    if (dwarf_formudata (attr, &fileidx) != 0 || fileidx >= nfiles)
666 	      continue;
667 
668 	    Dwarf_Word lineno;
669 	    attr = dwarf_attr (die, DW_AT_decl_line, &attr_mem);
670 	    if (dwarf_formudata (attr, &lineno) != 0 || lineno == 0)
671 	      continue;
672 
673 	    Dwarf_Addr lowpc;
674 	    Dwarf_Addr highpc;
675 	    if (tag == DW_TAG_subprogram)
676 	      {
677 		if (dwarf_lowpc (die, &lowpc) != 0
678 		    || dwarf_highpc (die, &highpc) != 0)
679 		  continue;
680 	      }
681 	    else
682 	      {
683 		if (get_var_range (die, &lowpc, &highpc) != 0)
684 		  continue;
685 	      }
686 
687 	    /* We have all the information.  Create a record.  */
688 	    struct local_name *newp
689 	      = (struct local_name *) xmalloc (sizeof (*newp));
690 	    newp->name = name;
691 	    newp->file = dwarf_filesrc (files, fileidx, NULL, NULL);
692 	    newp->lineno = lineno;
693 	    newp->lowpc = lowpc;
694 	    newp->highpc = highpc;
695 
696 	    /* Since we cannot deallocate individual memory we do not test
697 	       for duplicates in the tree.  This should not happen anyway.  */
698 	    if (tsearch (newp, &local_root, local_compare) == NULL)
699 	      error (EXIT_FAILURE, errno,
700 		     gettext ("cannot create search tree"));
701 	  }
702 	while (dwarf_siblingof (die, die) == 0);
703     }
704 }
705 
706 /* Do elf_strptr, but return a backup string and never NULL.  */
707 static const char *
sym_name(Elf * elf,GElf_Word strndx,GElf_Word st_name,char buf[],size_t n)708 sym_name (Elf *elf, GElf_Word strndx, GElf_Word st_name, char buf[], size_t n)
709 {
710   const char *symstr = elf_strptr (elf, strndx, st_name);
711   if (symstr == NULL)
712     {
713       snprintf (buf, n, "[invalid st_name %#" PRIx32 "]", st_name);
714       symstr = buf;
715     }
716   return symstr;
717 }
718 
719 /* Show symbols in SysV format.  */
720 static void
show_symbols_sysv(Ebl * ebl,GElf_Word strndx,const char * prefix,const char * fname,const char * fullname,GElf_SymX * syms,size_t nsyms,int longest_name,int longest_where)721 show_symbols_sysv (Ebl *ebl, GElf_Word strndx,
722 		   const char *prefix, const char *fname, const char *fullname,
723 		   GElf_SymX *syms, size_t nsyms, int longest_name,
724 		   int longest_where)
725 {
726   size_t shnum;
727   if (elf_getshnum (ebl->elf, &shnum) < 0)
728     INTERNAL_ERROR (fullname);
729 
730   bool scnnames_malloced = shnum * sizeof (const char *) > 128 * 1024;
731   const char **scnnames;
732   if (scnnames_malloced)
733     scnnames = (const char **) xmalloc (sizeof (const char *) * shnum);
734   else
735     scnnames = (const char **) alloca (sizeof (const char *) * shnum);
736   /* Get the section header string table index.  */
737   size_t shstrndx;
738   if (elf_getshstrndx (ebl->elf, &shstrndx) < 0)
739     error (EXIT_FAILURE, 0,
740 	   gettext ("cannot get section header string table index"));
741 
742   /* Cache the section names.  */
743   Elf_Scn *scn = NULL;
744   size_t cnt = 1;
745   while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
746     {
747       GElf_Shdr shdr_mem;
748 
749       assert (elf_ndxscn (scn) == cnt++);
750 
751       char *name = elf_strptr (ebl->elf, shstrndx,
752 			       gelf_getshdr (scn, &shdr_mem)->sh_name);
753       if (unlikely (name == NULL))
754 	{
755 	  name = alloca (sizeof "[invalid sh_name 0x12345678]");
756 	  snprintf (name, sizeof name, "[invalid sh_name %#" PRIx32 "]",
757 		    gelf_getshdr (scn, &shdr_mem)->sh_name);
758 	}
759       scnnames[elf_ndxscn (scn)] = name;
760     }
761 
762   int digits = length_map[gelf_getclass (ebl->elf) - 1][radix];
763 
764   /* We always print this prolog.  */
765   if (prefix == NULL || 1)
766     printf (gettext ("\n\nSymbols from %s:\n\n"), fullname);
767   else
768     printf (gettext ("\n\nSymbols from %s[%s]:\n\n"), prefix, fname);
769 
770   /* The header line.  */
771   printf (gettext ("%*s%-*s %-*s Class  Type     %-*s %*s Section\n\n"),
772 	  print_file_name ? (int) strlen (fullname) + 1: 0, "",
773 	  longest_name, sgettext ("sysv|Name"),
774 	  /* TRANS: the "sysv|" parts makes the string unique.  */
775 	  digits, sgettext ("sysv|Value"),
776 	  /* TRANS: the "sysv|" parts makes the string unique.  */
777 	  digits, sgettext ("sysv|Size"),
778 	  /* TRANS: the "sysv|" parts makes the string unique.  */
779 	  longest_where, sgettext ("sysv|Line"));
780 
781   /* Which format string to use (different radix for numbers).  */
782   const char *fmtstr;
783   if (radix == radix_hex)
784     fmtstr = "%-*s|%0*" PRIx64 "|%-6s|%-8s|%*" PRIx64 "|%*s|%s\n";
785   else if (radix == radix_decimal)
786     fmtstr = "%-*s|%*" PRId64 "|%-6s|%-8s|%*" PRId64 "|%*s|%s\n";
787   else
788     fmtstr = "%-*s|%0*" PRIo64 "|%-6s|%-8s|%*" PRIo64 "|%*s|%s\n";
789 
790   /* Iterate over all symbols.  */
791   for (cnt = 0; cnt < nsyms; ++cnt)
792     {
793       char symstrbuf[50];
794       const char *symstr = sym_name (ebl->elf, strndx, syms[cnt].sym.st_name,
795 				     symstrbuf, sizeof symstrbuf);
796 
797       char symbindbuf[50];
798       char symtypebuf[50];
799       char secnamebuf[1024];
800 
801       /* If we have to precede the line with the file name.  */
802       if (print_file_name)
803 	{
804 	  fputs_unlocked (fullname, stdout);
805 	  putchar_unlocked (':');
806 	}
807 
808       /* Print the actual string.  */
809       printf (fmtstr,
810 	      longest_name, symstr,
811 	      digits, syms[cnt].sym.st_value,
812 	      ebl_symbol_binding_name (ebl,
813 				       GELF_ST_BIND (syms[cnt].sym.st_info),
814 				       symbindbuf, sizeof (symbindbuf)),
815 	      ebl_symbol_type_name (ebl, GELF_ST_TYPE (syms[cnt].sym.st_info),
816 				    symtypebuf, sizeof (symtypebuf)),
817 	      digits, syms[cnt].sym.st_size, longest_where, syms[cnt].where,
818 	      ebl_section_name (ebl, syms[cnt].sym.st_shndx, syms[cnt].xndx,
819 				secnamebuf, sizeof (secnamebuf), scnnames,
820 				shnum));
821     }
822 
823   if (scnnames_malloced)
824     free (scnnames);
825 }
826 
827 
828 static char
class_type_char(GElf_Sym * sym)829 class_type_char (GElf_Sym *sym)
830 {
831   int local_p = GELF_ST_BIND (sym->st_info) == STB_LOCAL;
832 
833   /* XXX Add support for architecture specific types and classes.  */
834   if (sym->st_shndx == SHN_ABS)
835     return local_p ? 'a' : 'A';
836 
837   if (sym->st_shndx == SHN_UNDEF)
838     /* Undefined symbols must be global.  */
839     return 'U';
840 
841   char result = "NDTSFB          "[GELF_ST_TYPE (sym->st_info)];
842 
843   return local_p ? tolower (result) : result;
844 }
845 
846 
847 static void
show_symbols_bsd(Elf * elf,GElf_Word strndx,const char * prefix,const char * fname,const char * fullname,GElf_SymX * syms,size_t nsyms)848 show_symbols_bsd (Elf *elf, GElf_Word strndx,
849 		  const char *prefix, const char *fname, const char *fullname,
850 		  GElf_SymX *syms, size_t nsyms)
851 {
852   int digits = length_map[gelf_getclass (elf) - 1][radix];
853 
854   if (prefix != NULL && ! print_file_name)
855     printf ("\n%s:\n", fname);
856 
857   static const char *const fmtstrs[] =
858     {
859       [radix_hex] = "%0*" PRIx64 " %c%s %s\n",
860       [radix_decimal] = "%*" PRId64 " %c%s %s\n",
861       [radix_octal] = "%0*" PRIo64 " %c%s %s\n"
862     };
863   static const char *const sfmtstrs[] =
864     {
865       [radix_hex] = "%2$0*1$" PRIx64 " %7$0*6$" PRIx64 " %3$c%4$s %5$s\n",
866       [radix_decimal] = "%2$*1$" PRId64 " %7$*6$" PRId64 " %3$c%4$s %5$s\n",
867       [radix_octal] = "%2$0*1$" PRIo64 " %7$0*6$" PRIo64 " %3$c%4$s %5$s\n"
868     };
869 
870   /* Iterate over all symbols.  */
871   for (size_t cnt = 0; cnt < nsyms; ++cnt)
872     {
873       char symstrbuf[50];
874       const char *symstr = sym_name (elf, strndx, syms[cnt].sym.st_name,
875 				     symstrbuf, sizeof symstrbuf);
876 
877       /* Printing entries with a zero-length name makes the output
878 	 not very well parseable.  Since these entries don't carry
879 	 much information we leave them out.  */
880       if (symstr[0] == '\0')
881 	continue;
882 
883       /* If we have to precede the line with the file name.  */
884       if (print_file_name)
885 	{
886 	  fputs_unlocked (fullname, stdout);
887 	  putchar_unlocked (':');
888 	}
889 
890       if (syms[cnt].sym.st_shndx == SHN_UNDEF)
891 	printf ("%*s U%s %s\n",
892 		digits, "",
893 		mark_weak
894 		? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK
895 		   ? "*" : " ")
896 		: "",
897 		symstr);
898       else
899 	printf (print_size ? sfmtstrs[radix] : fmtstrs[radix],
900 		digits, syms[cnt].sym.st_value,
901 		class_type_char (&syms[cnt].sym),
902 		mark_weak
903 		? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK
904 		   ? "*" : " ")
905 		: "",
906 		symstr,
907 		digits, (uint64_t) syms[cnt].sym.st_size);
908     }
909 }
910 
911 
912 static void
show_symbols_posix(Elf * elf,GElf_Word strndx,const char * prefix,const char * fullname,GElf_SymX * syms,size_t nsyms)913 show_symbols_posix (Elf *elf, GElf_Word strndx, const char *prefix,
914 		    const char *fullname, GElf_SymX *syms, size_t nsyms)
915 {
916   if (prefix != NULL && ! print_file_name)
917     printf ("%s:\n", fullname);
918 
919   const char *fmtstr;
920   if (radix == radix_hex)
921     fmtstr = "%s %c%s %0*" PRIx64 " %0*" PRIx64 "\n";
922   else if (radix == radix_decimal)
923     fmtstr = "%s %c%s %*" PRId64 " %*" PRId64 "\n";
924   else
925     fmtstr = "%s %c%s %0*" PRIo64 " %0*" PRIo64 "\n";
926 
927   int digits = length_map[gelf_getclass (elf) - 1][radix];
928 
929   /* Iterate over all symbols.  */
930   for (size_t cnt = 0; cnt < nsyms; ++cnt)
931     {
932       char symstrbuf[50];
933       const char *symstr = sym_name (elf, strndx, syms[cnt].sym.st_name,
934 				     symstrbuf, sizeof symstrbuf);
935 
936       /* Printing entries with a zero-length name makes the output
937 	 not very well parseable.  Since these entries don't carry
938 	 much information we leave them out.  */
939       if (symstr[0] == '\0')
940 	continue;
941 
942       /* If we have to precede the line with the file name.  */
943       if (print_file_name)
944 	{
945 	  fputs_unlocked (fullname, stdout);
946 	  putchar_unlocked (':');
947 	  putchar_unlocked (' ');
948 	}
949 
950       printf (fmtstr,
951 	      symstr,
952 	      class_type_char (&syms[cnt].sym),
953 	      mark_weak
954 	      ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK ? "*" : " ")
955 	      : "",
956 	      digits, syms[cnt].sym.st_value,
957 	      digits, syms[cnt].sym.st_size);
958     }
959 }
960 
961 
962 /* Maximum size of memory we allocate on the stack.  */
963 #define MAX_STACK_ALLOC	65536
964 
965 static int
sort_by_address(const void * p1,const void * p2)966 sort_by_address (const void *p1, const void *p2)
967 {
968   GElf_SymX *s1 = (GElf_SymX *) p1;
969   GElf_SymX *s2 = (GElf_SymX *) p2;
970 
971   int result = (s1->sym.st_value < s2->sym.st_value
972 		? -1 : (s1->sym.st_value == s2->sym.st_value ? 0 : 1));
973 
974   return reverse_sort ? -result : result;
975 }
976 
977 static Elf_Data *sort_by_name_strtab;
978 
979 static int
sort_by_name(const void * p1,const void * p2)980 sort_by_name (const void *p1, const void *p2)
981 {
982   GElf_SymX *s1 = (GElf_SymX *) p1;
983   GElf_SymX *s2 = (GElf_SymX *) p2;
984 
985   const char *n1 = sort_by_name_strtab->d_buf + s1->sym.st_name;
986   const char *n2 = sort_by_name_strtab->d_buf + s2->sym.st_name;
987 
988   int result = strcmp (n1, n2);
989 
990   return reverse_sort ? -result : result;
991 }
992 
993 static void
show_symbols(Ebl * ebl,GElf_Ehdr * ehdr,Elf_Scn * scn,Elf_Scn * xndxscn,GElf_Shdr * shdr,const char * prefix,const char * fname,const char * fullname)994 show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
995 	      GElf_Shdr *shdr, const char *prefix, const char *fname,
996 	      const char *fullname)
997 {
998   /* Get the section header string table index.  */
999   size_t shstrndx;
1000   if (elf_getshstrndx (ebl->elf, &shstrndx) < 0)
1001     error (EXIT_FAILURE, 0,
1002 	   gettext ("cannot get section header string table index"));
1003 
1004   /* The section is that large.  */
1005   size_t size = shdr->sh_size;
1006   /* One entry is this large.  */
1007   size_t entsize = shdr->sh_entsize;
1008 
1009   /* Consistency checks.  */
1010   if (entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, ehdr->e_version))
1011     error (0, 0,
1012 	   gettext ("%s: entry size in section `%s' is not what we expect"),
1013 	   fullname, elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
1014   else if (size % entsize != 0)
1015     error (0, 0,
1016 	   gettext ("%s: size of section `%s' is not multiple of entry size"),
1017 	   fullname, elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
1018 
1019   /* Compute number of entries.  Handle buggy entsize values.  */
1020   size_t nentries = size / (entsize ?: 1);
1021 
1022 
1023 #define obstack_chunk_alloc xmalloc
1024 #define obstack_chunk_free free
1025   struct obstack whereob;
1026   obstack_init (&whereob);
1027 
1028   /* Get a DWARF debugging descriptor.  It's no problem if this isn't
1029      possible.  We just won't print any line number information.  */
1030   Dwarf *dbg = NULL;
1031   if (format == format_sysv)
1032     {
1033       dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL);
1034       if (dbg != NULL)
1035 	{
1036 	  (void) dwarf_getpubnames (dbg, get_global, NULL, 0);
1037 
1038 	  get_local_names (dbg);
1039 	}
1040     }
1041 
1042   /* Allocate the memory.
1043 
1044      XXX We can use a dirty trick here.  Since GElf_Sym == Elf64_Sym we
1045      can use the data memory instead of copying again if what we read
1046      is a 64 bit file.  */
1047   GElf_SymX *sym_mem;
1048   if (nentries * sizeof (GElf_SymX) < MAX_STACK_ALLOC)
1049     sym_mem = (GElf_SymX *) alloca (nentries * sizeof (GElf_SymX));
1050   else
1051     sym_mem = (GElf_SymX *) xmalloc (nentries * sizeof (GElf_SymX));
1052 
1053   /* Get the data of the section.  */
1054   Elf_Data *data = elf_getdata (scn, NULL);
1055   Elf_Data *xndxdata = elf_getdata (xndxscn, NULL);
1056   if (data == NULL || (xndxscn != NULL && xndxdata == NULL))
1057     INTERNAL_ERROR (fullname);
1058 
1059   /* Iterate over all symbols.  */
1060   int longest_name = 4;
1061   int longest_where = 4;
1062   size_t nentries_used = 0;
1063   for (size_t cnt = 0; cnt < nentries; ++cnt)
1064     {
1065       GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, cnt,
1066 					&sym_mem[nentries_used].sym,
1067 					&sym_mem[nentries_used].xndx);
1068       if (sym == NULL)
1069 	INTERNAL_ERROR (fullname);
1070 
1071       /* Filter out administrative symbols without a name and those
1072 	 deselected by ther user with command line options.  */
1073       if ((hide_undefined && sym->st_shndx == SHN_UNDEF)
1074 	  || (hide_defined && sym->st_shndx != SHN_UNDEF)
1075 	  || (hide_local && GELF_ST_BIND (sym->st_info) == STB_LOCAL))
1076 	continue;
1077 
1078       sym_mem[nentries_used].where = "";
1079       if (format == format_sysv)
1080 	{
1081 	  const char *symstr = elf_strptr (ebl->elf, shdr->sh_link,
1082 					   sym->st_name);
1083 	  if (symstr == NULL)
1084 	    continue;
1085 
1086 	  longest_name = MAX ((size_t) longest_name, strlen (symstr));
1087 
1088 	  if (sym->st_shndx != SHN_UNDEF
1089 	      && GELF_ST_BIND (sym->st_info) != STB_LOCAL
1090 	      && global_root != NULL)
1091 	    {
1092 	      Dwarf_Global fake = { .name = symstr };
1093 	      Dwarf_Global **found = tfind (&fake, &global_root,
1094 					    global_compare);
1095 	      if (found != NULL)
1096 		{
1097 		  Dwarf_Die die_mem;
1098 		  Dwarf_Die *die = dwarf_offdie (dbg, (*found)->die_offset,
1099 						 &die_mem);
1100 
1101 		  Dwarf_Die cudie_mem;
1102 		  Dwarf_Die *cudie = NULL;
1103 
1104 		  Dwarf_Addr lowpc;
1105 		  Dwarf_Addr highpc;
1106 		  if (die != NULL
1107 		      && dwarf_lowpc (die, &lowpc) == 0
1108 		      && lowpc <= sym->st_value
1109 		      && dwarf_highpc (die, &highpc) == 0
1110 		      && highpc > sym->st_value)
1111 		    cudie = dwarf_offdie (dbg, (*found)->cu_offset,
1112 					  &cudie_mem);
1113 		  if (cudie != NULL)
1114 		    {
1115 		      Dwarf_Line *line = dwarf_getsrc_die (cudie,
1116 							   sym->st_value);
1117 		      if (line != NULL)
1118 			{
1119 			  /* We found the line.  */
1120 			  int lineno;
1121 			  (void) dwarf_lineno (line, &lineno);
1122 			  int n;
1123 			  n = obstack_printf (&whereob, "%s:%d%c",
1124 					      basename (dwarf_linesrc (line,
1125 								       NULL,
1126 								       NULL)),
1127 					      lineno, '\0');
1128 			  sym_mem[nentries_used].where
1129 			    = obstack_finish (&whereob);
1130 
1131 			  /* The return value of obstack_print included the
1132 			     NUL byte, so subtract one.  */
1133 			  if (--n > (int) longest_where)
1134 			    longest_where = (size_t) n;
1135 			}
1136 		    }
1137 		}
1138 	    }
1139 
1140 	  /* Try to find the symol among the local symbols.  */
1141 	  if (sym_mem[nentries_used].where[0] == '\0')
1142 	    {
1143 	      struct local_name fake =
1144 		{
1145 		  .name = symstr,
1146 		  .lowpc = sym->st_value,
1147 		  .highpc = sym->st_value,
1148 		};
1149 	      struct local_name **found = tfind (&fake, &local_root,
1150 						 local_compare);
1151 	      if (found != NULL)
1152 		{
1153 		  /* We found the line.  */
1154 		  int n = obstack_printf (&whereob, "%s:%" PRIu64 "%c",
1155 					  basename ((*found)->file),
1156 					  (*found)->lineno,
1157 					  '\0');
1158 		  sym_mem[nentries_used].where = obstack_finish (&whereob);
1159 
1160 		  /* The return value of obstack_print included the
1161 		     NUL byte, so subtract one.  */
1162 		  if (--n > (int) longest_where)
1163 		    longest_where = (size_t) n;
1164 		}
1165 	    }
1166 	}
1167 
1168       /* We use this entry.  */
1169       ++nentries_used;
1170     }
1171   /* Now we know the exact number.  */
1172   nentries = nentries_used;
1173 
1174   /* Sort the entries according to the users wishes.  */
1175   if (sort == sort_name)
1176     {
1177       sort_by_name_strtab = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link),
1178 					 NULL);
1179       qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_name);
1180     }
1181   else if (sort == sort_numeric)
1182     qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_address);
1183 
1184   /* Finally print according to the users selection.  */
1185   switch (format)
1186     {
1187     case format_sysv:
1188       show_symbols_sysv (ebl, shdr->sh_link, prefix, fname,
1189 			 fullname, sym_mem, nentries, longest_name,
1190 			 longest_where);
1191       break;
1192 
1193     case format_bsd:
1194       show_symbols_bsd (ebl->elf, shdr->sh_link, prefix, fname, fullname,
1195 			sym_mem, nentries);
1196       break;
1197 
1198     case format_posix:
1199     default:
1200       assert (format == format_posix);
1201       show_symbols_posix (ebl->elf, shdr->sh_link, prefix, fullname, sym_mem,
1202 			  nentries);
1203       break;
1204     }
1205 
1206   /* Free all memory.  */
1207   if (nentries * sizeof (GElf_Sym) >= MAX_STACK_ALLOC)
1208     free (sym_mem);
1209 
1210   obstack_free (&whereob, NULL);
1211 
1212   if (dbg != NULL)
1213     {
1214       tdestroy (global_root, free);
1215       global_root = NULL;
1216 
1217       tdestroy (local_root, free);
1218       local_root = NULL;
1219 
1220       (void) dwarf_end (dbg);
1221     }
1222 }
1223 
1224 
1225 static int
handle_elf(Elf * elf,const char * prefix,const char * fname,const char * suffix)1226 handle_elf (Elf *elf, const char *prefix, const char *fname,
1227 	    const char *suffix)
1228 {
1229   size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
1230   size_t suffix_len = suffix == NULL ? 0 : strlen (suffix);
1231   size_t fname_len = strlen (fname) + 1;
1232   char fullname[prefix_len + 1 + fname_len + suffix_len];
1233   char *cp = fullname;
1234   Elf_Scn *scn = NULL;
1235   int any = 0;
1236   int result = 0;
1237   GElf_Ehdr ehdr_mem;
1238   GElf_Ehdr *ehdr;
1239   Ebl *ebl;
1240 
1241   /* Get the backend for this object file type.  */
1242   ebl = ebl_openbackend (elf);
1243 
1244   /* We need the ELF header in a few places.  */
1245   ehdr = gelf_getehdr (elf, &ehdr_mem);
1246   if (ehdr == NULL)
1247     INTERNAL_ERROR (fullname);
1248 
1249   /* If we are asked to print the dynamic symbol table and this is
1250      executable or dynamic executable, fail.  */
1251   if (symsec_type == SHT_DYNSYM
1252       && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
1253     {
1254       /* XXX Add machine specific object file types.  */
1255       error (0, 0, gettext ("%s%s%s%s: Invalid operation"),
1256 	     prefix ?: "", prefix ? "(" : "", fname, prefix ? ")" : "");
1257       result = 1;
1258       goto out;
1259     }
1260 
1261   /* Create the full name of the file.  */
1262   if (prefix != NULL)
1263     cp = mempcpy (cp, prefix, prefix_len);
1264   cp = mempcpy (cp, fname, fname_len);
1265   if (suffix != NULL)
1266     memcpy (cp - 1, suffix, suffix_len + 1);
1267 
1268   /* Find the symbol table.
1269 
1270      XXX Can there be more than one?  Do we print all?  Currently we do.  */
1271   while ((scn = elf_nextscn (elf, scn)) != NULL)
1272     {
1273       GElf_Shdr shdr_mem;
1274       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
1275 
1276       if (shdr == NULL)
1277 	INTERNAL_ERROR (fullname);
1278 
1279       if (shdr->sh_type == symsec_type)
1280 	{
1281 	  Elf_Scn *xndxscn = NULL;
1282 
1283 	  /* We have a symbol table.  First make sure we remember this.  */
1284 	  any = 1;
1285 
1286 	  /* Look for an extended section index table for this section.  */
1287 	  if (symsec_type == SHT_SYMTAB)
1288 	    {
1289 	      size_t scnndx = elf_ndxscn (scn);
1290 
1291 	      while ((xndxscn = elf_nextscn (elf, xndxscn)) != NULL)
1292 		{
1293 		  GElf_Shdr xndxshdr_mem;
1294 		  GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem);
1295 
1296 		  if (xndxshdr == NULL)
1297 		    INTERNAL_ERROR (fullname);
1298 
1299 		  if (xndxshdr->sh_type == SHT_SYMTAB_SHNDX
1300 		      && xndxshdr->sh_link == scnndx)
1301 		    break;
1302 		}
1303 	    }
1304 
1305 	  show_symbols (ebl, ehdr, scn, xndxscn, shdr, prefix, fname,
1306 			fullname);
1307 	}
1308     }
1309 
1310   if (! any)
1311     {
1312       error (0, 0, gettext ("%s%s%s: no symbols"),
1313 	     prefix ?: "", prefix ? ":" : "", fname);
1314       result = 1;
1315     }
1316 
1317  out:
1318   /* Close the ELF backend library descriptor.  */
1319   ebl_closebackend (ebl);
1320 
1321   return result;
1322 }
1323 
1324 
1325 #include "debugpred.h"
1326