blob: f78861ece683256437fb22ca5c47de0badd40a7a [file] [log] [blame]
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00001/* Print symbol information from ELF file in human-readable form.
Ulrich Drepper44173ed2009-01-01 19:00:41 -08002 Copyright (C) 2000-2008, 2009 Red Hat, Inc.
Ulrich Drepper361df7d2006-04-04 21:38:57 +00003 This file is part of Red Hat elfutils.
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00004 Written by Ulrich Drepper <drepper@redhat.com>, 2000.
5
Ulrich Drepper361df7d2006-04-04 21:38:57 +00006 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.
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00009
Ulrich Drepper361df7d2006-04-04 21:38:57 +000010 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,
Ulrich Drepper1e9ef502006-04-04 22:29:06 +000017 Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA.
Ulrich Drepper361df7d2006-04-04 21:38:57 +000018
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>. */
Ulrich Drepperb08d5a82005-07-26 05:00:05 +000026
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. */
60static void print_version (FILE *stream, struct argp_state *state);
Ulrich Drepperfdc93e12009-01-17 11:47:10 -080061ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
Ulrich Drepperb08d5a82005-07-26 05:00:05 +000062
63/* Bug report address. */
Ulrich Drepperfdc93e12009-01-17 11:47:10 -080064ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
Ulrich Drepperb08d5a82005-07-26 05:00:05 +000065
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. */
72static 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. */
108static const char doc[] = N_("List symbols from FILEs (a.out by default).");
109
110/* Strings for arguments in help texts. */
111static const char args_doc[] = N_("[FILE...]");
112
113/* Prototype for option handler. */
114static error_t parse_opt (int key, char *arg, struct argp_state *state);
115
116/* Data structure to communicate with argp functions. */
117static struct argp argp =
118{
119 options, parse_opt, args_doc, doc, NULL, NULL, NULL
120};
121
122
123/* Print symbols in file named FNAME. */
124static int process_file (const char *fname, bool more_than_one);
125
126/* Handle content of archive. */
127static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname,
128 const char *suffix);
129
130/* Handle ELF file. */
131static 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"), \
Ulrich Drepperb0243862007-06-06 00:09:36 +0000137 fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1))
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000138
139
140/* Internal representation of symbols. */
141typedef 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. */
152static enum
153{
154 format_sysv = 0,
155 format_bsd,
156 format_posix
157} format;
158
159/* Print defined, undefined, or both? */
160static bool hide_undefined;
161static bool hide_defined;
162
163/* Print local symbols also? */
164static bool hide_local;
165
166/* Nonzero if full filename should precede every symbol. */
167static bool print_file_name;
168
169/* If true print size of defined symbols in BSD format. */
170static bool print_size;
171
172/* If true print archive index. */
173static bool print_armap;
174
175/* If true reverse sorting. */
176static bool reverse_sort;
177
178/* Type of the section we are printing. */
179static GElf_Word symsec_type = SHT_SYMTAB;
180
181/* Sorting selection. */
182static enum
183{
184 sort_name = 0,
185 sort_numeric,
186 sort_nosort
187} sort;
188
189/* Radix for printed numbers. */
190static 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. */
199static bool mark_weak;
200
201
202int
203main (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. */
Ulrich Drepperb0243862007-06-06 00:09:36 +0000220 (void) bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000221
222 /* Initialize the message catalog. */
Ulrich Drepperb0243862007-06-06 00:09:36 +0000223 (void) textdomain (PACKAGE_TARNAME);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000224
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. */
249static void
250print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
251{
Ulrich Drepperb0243862007-06-06 00:09:36 +0000252 fprintf (stream, "nm (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000253 fprintf (stream, gettext ("\
254Copyright (C) %s Red Hat, Inc.\n\
255This is free software; see the source for copying conditions. There is NO\n\
256warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
Ulrich Drepper44173ed2009-01-01 19:00:41 -0800257"), "2009");
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000258 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
259}
260
261
262/* Handle program arguments. */
263static error_t
264parse_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. */
356static int
357process_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)
Ulrich Drepperd112ef82005-09-03 21:31:27 +0000380 error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000381
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)
Ulrich Drepperd112ef82005-09-03 21:31:27 +0000392 error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000393
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
408static int
409handle_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. */
505static 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
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000522static int
523global_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
532static void *global_root;
533
534
535static int
536get_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
547struct 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
557static int
558local_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
589static int
590get_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
Roland McGrath6724c902005-10-28 07:07:19 +0000597 Dwarf_Op *loc;
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000598 size_t nloc;
Roland McGrath6724c902005-10-28 07:07:19 +0000599 if (dwarf_getlocation (locattr, &loc, &nloc) != 0)
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000600 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
615static void *local_root;
616
617
618static void
619get_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
Roland McGrath468fe4d2008-12-11 21:00:12 -0800706/* Do elf_strptr, but return a backup string and never NULL. */
707static const char *
708sym_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}
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000718
719/* Show symbols in SysV format. */
720static void
Marek Polacekc8920de2011-05-12 12:08:21 +0200721show_symbols_sysv (Ebl *ebl, GElf_Word strndx, const char *fullname,
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000722 GElf_SymX *syms, size_t nsyms, int longest_name,
723 int longest_where)
724{
725 size_t shnum;
Ulrich Drepperf1894932009-06-13 15:55:42 -0700726 if (elf_getshdrnum (ebl->elf, &shnum) < 0)
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000727 INTERNAL_ERROR (fullname);
728
729 bool scnnames_malloced = shnum * sizeof (const char *) > 128 * 1024;
730 const char **scnnames;
731 if (scnnames_malloced)
732 scnnames = (const char **) xmalloc (sizeof (const char *) * shnum);
733 else
734 scnnames = (const char **) alloca (sizeof (const char *) * shnum);
735 /* Get the section header string table index. */
736 size_t shstrndx;
Ulrich Drepperf1894932009-06-13 15:55:42 -0700737 if (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000738 error (EXIT_FAILURE, 0,
739 gettext ("cannot get section header string table index"));
740
741 /* Cache the section names. */
742 Elf_Scn *scn = NULL;
743 size_t cnt = 1;
744 while ((scn = elf_nextscn (ebl->elf, scn)) != NULL)
745 {
746 GElf_Shdr shdr_mem;
747
748 assert (elf_ndxscn (scn) == cnt++);
749
Roland McGrath468fe4d2008-12-11 21:00:12 -0800750 char *name = elf_strptr (ebl->elf, shstrndx,
751 gelf_getshdr (scn, &shdr_mem)->sh_name);
752 if (unlikely (name == NULL))
753 {
754 name = alloca (sizeof "[invalid sh_name 0x12345678]");
755 snprintf (name, sizeof name, "[invalid sh_name %#" PRIx32 "]",
756 gelf_getshdr (scn, &shdr_mem)->sh_name);
757 }
758 scnnames[elf_ndxscn (scn)] = name;
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000759 }
760
761 int digits = length_map[gelf_getclass (ebl->elf) - 1][radix];
762
763 /* We always print this prolog. */
Marek Polacekc8920de2011-05-12 12:08:21 +0200764 printf (gettext ("\n\nSymbols from %s:\n\n"), fullname);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000765
766 /* The header line. */
767 printf (gettext ("%*s%-*s %-*s Class Type %-*s %*s Section\n\n"),
768 print_file_name ? (int) strlen (fullname) + 1: 0, "",
769 longest_name, sgettext ("sysv|Name"),
770 /* TRANS: the "sysv|" parts makes the string unique. */
771 digits, sgettext ("sysv|Value"),
772 /* TRANS: the "sysv|" parts makes the string unique. */
773 digits, sgettext ("sysv|Size"),
774 /* TRANS: the "sysv|" parts makes the string unique. */
775 longest_where, sgettext ("sysv|Line"));
776
777 /* Which format string to use (different radix for numbers). */
778 const char *fmtstr;
779 if (radix == radix_hex)
780 fmtstr = "%-*s|%0*" PRIx64 "|%-6s|%-8s|%*" PRIx64 "|%*s|%s\n";
781 else if (radix == radix_decimal)
782 fmtstr = "%-*s|%*" PRId64 "|%-6s|%-8s|%*" PRId64 "|%*s|%s\n";
783 else
784 fmtstr = "%-*s|%0*" PRIo64 "|%-6s|%-8s|%*" PRIo64 "|%*s|%s\n";
785
786 /* Iterate over all symbols. */
787 for (cnt = 0; cnt < nsyms; ++cnt)
788 {
Roland McGrath468fe4d2008-12-11 21:00:12 -0800789 char symstrbuf[50];
790 const char *symstr = sym_name (ebl->elf, strndx, syms[cnt].sym.st_name,
791 symstrbuf, sizeof symstrbuf);
792
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000793 char symbindbuf[50];
794 char symtypebuf[50];
795 char secnamebuf[1024];
796
797 /* If we have to precede the line with the file name. */
798 if (print_file_name)
799 {
800 fputs_unlocked (fullname, stdout);
801 putchar_unlocked (':');
802 }
803
804 /* Print the actual string. */
805 printf (fmtstr,
806 longest_name, symstr,
807 digits, syms[cnt].sym.st_value,
808 ebl_symbol_binding_name (ebl,
809 GELF_ST_BIND (syms[cnt].sym.st_info),
810 symbindbuf, sizeof (symbindbuf)),
811 ebl_symbol_type_name (ebl, GELF_ST_TYPE (syms[cnt].sym.st_info),
812 symtypebuf, sizeof (symtypebuf)),
813 digits, syms[cnt].sym.st_size, longest_where, syms[cnt].where,
814 ebl_section_name (ebl, syms[cnt].sym.st_shndx, syms[cnt].xndx,
815 secnamebuf, sizeof (secnamebuf), scnnames,
816 shnum));
817 }
818
819 if (scnnames_malloced)
820 free (scnnames);
821}
822
823
824static char
825class_type_char (GElf_Sym *sym)
826{
827 int local_p = GELF_ST_BIND (sym->st_info) == STB_LOCAL;
828
829 /* XXX Add support for architecture specific types and classes. */
830 if (sym->st_shndx == SHN_ABS)
831 return local_p ? 'a' : 'A';
832
833 if (sym->st_shndx == SHN_UNDEF)
834 /* Undefined symbols must be global. */
835 return 'U';
836
837 char result = "NDTSFB "[GELF_ST_TYPE (sym->st_info)];
838
839 return local_p ? tolower (result) : result;
840}
841
842
843static void
844show_symbols_bsd (Elf *elf, GElf_Word strndx,
845 const char *prefix, const char *fname, const char *fullname,
846 GElf_SymX *syms, size_t nsyms)
847{
848 int digits = length_map[gelf_getclass (elf) - 1][radix];
849
850 if (prefix != NULL && ! print_file_name)
851 printf ("\n%s:\n", fname);
852
853 static const char *const fmtstrs[] =
854 {
855 [radix_hex] = "%0*" PRIx64 " %c%s %s\n",
856 [radix_decimal] = "%*" PRId64 " %c%s %s\n",
857 [radix_octal] = "%0*" PRIo64 " %c%s %s\n"
858 };
859 static const char *const sfmtstrs[] =
860 {
861 [radix_hex] = "%2$0*1$" PRIx64 " %7$0*6$" PRIx64 " %3$c%4$s %5$s\n",
862 [radix_decimal] = "%2$*1$" PRId64 " %7$*6$" PRId64 " %3$c%4$s %5$s\n",
863 [radix_octal] = "%2$0*1$" PRIo64 " %7$0*6$" PRIo64 " %3$c%4$s %5$s\n"
864 };
865
866 /* Iterate over all symbols. */
867 for (size_t cnt = 0; cnt < nsyms; ++cnt)
868 {
Roland McGrath468fe4d2008-12-11 21:00:12 -0800869 char symstrbuf[50];
870 const char *symstr = sym_name (elf, strndx, syms[cnt].sym.st_name,
871 symstrbuf, sizeof symstrbuf);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000872
873 /* Printing entries with a zero-length name makes the output
874 not very well parseable. Since these entries don't carry
875 much information we leave them out. */
876 if (symstr[0] == '\0')
877 continue;
878
879 /* If we have to precede the line with the file name. */
880 if (print_file_name)
881 {
882 fputs_unlocked (fullname, stdout);
883 putchar_unlocked (':');
884 }
885
886 if (syms[cnt].sym.st_shndx == SHN_UNDEF)
887 printf ("%*s U%s %s\n",
888 digits, "",
889 mark_weak
890 ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK
891 ? "*" : " ")
892 : "",
Roland McGrath468fe4d2008-12-11 21:00:12 -0800893 symstr);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000894 else
895 printf (print_size ? sfmtstrs[radix] : fmtstrs[radix],
896 digits, syms[cnt].sym.st_value,
897 class_type_char (&syms[cnt].sym),
898 mark_weak
899 ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK
900 ? "*" : " ")
901 : "",
Roland McGrath468fe4d2008-12-11 21:00:12 -0800902 symstr,
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000903 digits, (uint64_t) syms[cnt].sym.st_size);
904 }
905}
906
907
908static void
909show_symbols_posix (Elf *elf, GElf_Word strndx, const char *prefix,
910 const char *fullname, GElf_SymX *syms, size_t nsyms)
911{
912 if (prefix != NULL && ! print_file_name)
913 printf ("%s:\n", fullname);
914
915 const char *fmtstr;
916 if (radix == radix_hex)
917 fmtstr = "%s %c%s %0*" PRIx64 " %0*" PRIx64 "\n";
918 else if (radix == radix_decimal)
919 fmtstr = "%s %c%s %*" PRId64 " %*" PRId64 "\n";
920 else
921 fmtstr = "%s %c%s %0*" PRIo64 " %0*" PRIo64 "\n";
922
923 int digits = length_map[gelf_getclass (elf) - 1][radix];
924
925 /* Iterate over all symbols. */
926 for (size_t cnt = 0; cnt < nsyms; ++cnt)
927 {
Roland McGrath468fe4d2008-12-11 21:00:12 -0800928 char symstrbuf[50];
929 const char *symstr = sym_name (elf, strndx, syms[cnt].sym.st_name,
930 symstrbuf, sizeof symstrbuf);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000931
932 /* Printing entries with a zero-length name makes the output
933 not very well parseable. Since these entries don't carry
934 much information we leave them out. */
935 if (symstr[0] == '\0')
936 continue;
937
938 /* If we have to precede the line with the file name. */
939 if (print_file_name)
940 {
941 fputs_unlocked (fullname, stdout);
942 putchar_unlocked (':');
943 putchar_unlocked (' ');
944 }
945
946 printf (fmtstr,
947 symstr,
948 class_type_char (&syms[cnt].sym),
949 mark_weak
950 ? (GELF_ST_BIND (syms[cnt].sym.st_info) == STB_WEAK ? "*" : " ")
951 : "",
952 digits, syms[cnt].sym.st_value,
953 digits, syms[cnt].sym.st_size);
954 }
955}
956
957
958/* Maximum size of memory we allocate on the stack. */
959#define MAX_STACK_ALLOC 65536
960
Roland McGrathcb6d8652007-08-23 08:10:54 +0000961static int
962sort_by_address (const void *p1, const void *p2)
963{
964 GElf_SymX *s1 = (GElf_SymX *) p1;
965 GElf_SymX *s2 = (GElf_SymX *) p2;
966
967 int result = (s1->sym.st_value < s2->sym.st_value
968 ? -1 : (s1->sym.st_value == s2->sym.st_value ? 0 : 1));
969
970 return reverse_sort ? -result : result;
971}
972
973static Elf_Data *sort_by_name_strtab;
974
975static int
976sort_by_name (const void *p1, const void *p2)
977{
978 GElf_SymX *s1 = (GElf_SymX *) p1;
979 GElf_SymX *s2 = (GElf_SymX *) p2;
980
981 const char *n1 = sort_by_name_strtab->d_buf + s1->sym.st_name;
982 const char *n2 = sort_by_name_strtab->d_buf + s2->sym.st_name;
983
984 int result = strcmp (n1, n2);
985
986 return reverse_sort ? -result : result;
987}
988
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000989static void
990show_symbols (Ebl *ebl, GElf_Ehdr *ehdr, Elf_Scn *scn, Elf_Scn *xndxscn,
991 GElf_Shdr *shdr, const char *prefix, const char *fname,
992 const char *fullname)
993{
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000994 /* Get the section header string table index. */
995 size_t shstrndx;
Ulrich Drepperf1894932009-06-13 15:55:42 -0700996 if (elf_getshdrstrndx (ebl->elf, &shstrndx) < 0)
Ulrich Drepperb08d5a82005-07-26 05:00:05 +0000997 error (EXIT_FAILURE, 0,
998 gettext ("cannot get section header string table index"));
999
1000 /* The section is that large. */
1001 size_t size = shdr->sh_size;
1002 /* One entry is this large. */
1003 size_t entsize = shdr->sh_entsize;
1004
1005 /* Consistency checks. */
1006 if (entsize != gelf_fsize (ebl->elf, ELF_T_SYM, 1, ehdr->e_version))
1007 error (0, 0,
1008 gettext ("%s: entry size in section `%s' is not what we expect"),
1009 fullname, elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
1010 else if (size % entsize != 0)
1011 error (0, 0,
1012 gettext ("%s: size of section `%s' is not multiple of entry size"),
1013 fullname, elf_strptr (ebl->elf, shstrndx, shdr->sh_name));
1014
1015 /* Compute number of entries. Handle buggy entsize values. */
1016 size_t nentries = size / (entsize ?: 1);
1017
1018
1019#define obstack_chunk_alloc xmalloc
1020#define obstack_chunk_free free
1021 struct obstack whereob;
1022 obstack_init (&whereob);
1023
1024 /* Get a DWARF debugging descriptor. It's no problem if this isn't
1025 possible. We just won't print any line number information. */
1026 Dwarf *dbg = NULL;
1027 if (format == format_sysv)
1028 {
1029 dbg = dwarf_begin_elf (ebl->elf, DWARF_C_READ, NULL);
1030 if (dbg != NULL)
1031 {
1032 (void) dwarf_getpubnames (dbg, get_global, NULL, 0);
1033
1034 get_local_names (dbg);
1035 }
1036 }
1037
1038 /* Allocate the memory.
1039
1040 XXX We can use a dirty trick here. Since GElf_Sym == Elf64_Sym we
1041 can use the data memory instead of copying again if what we read
1042 is a 64 bit file. */
1043 GElf_SymX *sym_mem;
1044 if (nentries * sizeof (GElf_SymX) < MAX_STACK_ALLOC)
1045 sym_mem = (GElf_SymX *) alloca (nentries * sizeof (GElf_SymX));
1046 else
1047 sym_mem = (GElf_SymX *) xmalloc (nentries * sizeof (GElf_SymX));
1048
1049 /* Get the data of the section. */
1050 Elf_Data *data = elf_getdata (scn, NULL);
1051 Elf_Data *xndxdata = elf_getdata (xndxscn, NULL);
1052 if (data == NULL || (xndxscn != NULL && xndxdata == NULL))
1053 INTERNAL_ERROR (fullname);
1054
1055 /* Iterate over all symbols. */
1056 int longest_name = 4;
1057 int longest_where = 4;
1058 size_t nentries_used = 0;
1059 for (size_t cnt = 0; cnt < nentries; ++cnt)
1060 {
1061 GElf_Sym *sym = gelf_getsymshndx (data, xndxdata, cnt,
1062 &sym_mem[nentries_used].sym,
1063 &sym_mem[nentries_used].xndx);
1064 if (sym == NULL)
1065 INTERNAL_ERROR (fullname);
1066
1067 /* Filter out administrative symbols without a name and those
1068 deselected by ther user with command line options. */
1069 if ((hide_undefined && sym->st_shndx == SHN_UNDEF)
1070 || (hide_defined && sym->st_shndx != SHN_UNDEF)
1071 || (hide_local && GELF_ST_BIND (sym->st_info) == STB_LOCAL))
1072 continue;
1073
1074 sym_mem[nentries_used].where = "";
1075 if (format == format_sysv)
1076 {
1077 const char *symstr = elf_strptr (ebl->elf, shdr->sh_link,
1078 sym->st_name);
Roland McGrath468fe4d2008-12-11 21:00:12 -08001079 if (symstr == NULL)
1080 continue;
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00001081
1082 longest_name = MAX ((size_t) longest_name, strlen (symstr));
1083
1084 if (sym->st_shndx != SHN_UNDEF
1085 && GELF_ST_BIND (sym->st_info) != STB_LOCAL
1086 && global_root != NULL)
1087 {
1088 Dwarf_Global fake = { .name = symstr };
1089 Dwarf_Global **found = tfind (&fake, &global_root,
1090 global_compare);
1091 if (found != NULL)
1092 {
1093 Dwarf_Die die_mem;
1094 Dwarf_Die *die = dwarf_offdie (dbg, (*found)->die_offset,
1095 &die_mem);
1096
1097 Dwarf_Die cudie_mem;
1098 Dwarf_Die *cudie = NULL;
1099
1100 Dwarf_Addr lowpc;
1101 Dwarf_Addr highpc;
1102 if (die != NULL
1103 && dwarf_lowpc (die, &lowpc) == 0
1104 && lowpc <= sym->st_value
1105 && dwarf_highpc (die, &highpc) == 0
1106 && highpc > sym->st_value)
1107 cudie = dwarf_offdie (dbg, (*found)->cu_offset,
1108 &cudie_mem);
1109 if (cudie != NULL)
1110 {
1111 Dwarf_Line *line = dwarf_getsrc_die (cudie,
1112 sym->st_value);
1113 if (line != NULL)
1114 {
1115 /* We found the line. */
1116 int lineno;
1117 (void) dwarf_lineno (line, &lineno);
1118 int n;
1119 n = obstack_printf (&whereob, "%s:%d%c",
1120 basename (dwarf_linesrc (line,
1121 NULL,
1122 NULL)),
1123 lineno, '\0');
1124 sym_mem[nentries_used].where
1125 = obstack_finish (&whereob);
1126
1127 /* The return value of obstack_print included the
1128 NUL byte, so subtract one. */
1129 if (--n > (int) longest_where)
1130 longest_where = (size_t) n;
1131 }
1132 }
1133 }
1134 }
1135
1136 /* Try to find the symol among the local symbols. */
1137 if (sym_mem[nentries_used].where[0] == '\0')
1138 {
1139 struct local_name fake =
1140 {
1141 .name = symstr,
1142 .lowpc = sym->st_value,
1143 .highpc = sym->st_value,
1144 };
1145 struct local_name **found = tfind (&fake, &local_root,
1146 local_compare);
1147 if (found != NULL)
1148 {
1149 /* We found the line. */
1150 int n = obstack_printf (&whereob, "%s:%" PRIu64 "%c",
1151 basename ((*found)->file),
1152 (*found)->lineno,
1153 '\0');
1154 sym_mem[nentries_used].where = obstack_finish (&whereob);
1155
1156 /* The return value of obstack_print included the
1157 NUL byte, so subtract one. */
1158 if (--n > (int) longest_where)
1159 longest_where = (size_t) n;
1160 }
1161 }
1162 }
1163
1164 /* We use this entry. */
1165 ++nentries_used;
1166 }
1167 /* Now we know the exact number. */
1168 nentries = nentries_used;
1169
1170 /* Sort the entries according to the users wishes. */
1171 if (sort == sort_name)
Roland McGrathcb6d8652007-08-23 08:10:54 +00001172 {
1173 sort_by_name_strtab = elf_getdata (elf_getscn (ebl->elf, shdr->sh_link),
1174 NULL);
1175 qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_name);
1176 }
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00001177 else if (sort == sort_numeric)
1178 qsort (sym_mem, nentries, sizeof (GElf_SymX), sort_by_address);
1179
1180 /* Finally print according to the users selection. */
1181 switch (format)
1182 {
1183 case format_sysv:
Marek Polacekc8920de2011-05-12 12:08:21 +02001184 show_symbols_sysv (ebl, shdr->sh_link, fullname, sym_mem, nentries,
1185 longest_name, longest_where);
Ulrich Drepperb08d5a82005-07-26 05:00:05 +00001186 break;
1187
1188 case format_bsd:
1189 show_symbols_bsd (ebl->elf, shdr->sh_link, prefix, fname, fullname,
1190 sym_mem, nentries);
1191 break;
1192
1193 case format_posix:
1194 default:
1195 assert (format == format_posix);
1196 show_symbols_posix (ebl->elf, shdr->sh_link, prefix, fullname, sym_mem,
1197 nentries);
1198 break;
1199 }
1200
1201 /* Free all memory. */
1202 if (nentries * sizeof (GElf_Sym) >= MAX_STACK_ALLOC)
1203 free (sym_mem);
1204
1205 obstack_free (&whereob, NULL);
1206
1207 if (dbg != NULL)
1208 {
1209 tdestroy (global_root, free);
1210 global_root = NULL;
1211
1212 tdestroy (local_root, free);
1213 local_root = NULL;
1214
1215 (void) dwarf_end (dbg);
1216 }
1217}
1218
1219
1220static int
1221handle_elf (Elf *elf, const char *prefix, const char *fname,
1222 const char *suffix)
1223{
1224 size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
1225 size_t suffix_len = suffix == NULL ? 0 : strlen (suffix);
1226 size_t fname_len = strlen (fname) + 1;
1227 char fullname[prefix_len + 1 + fname_len + suffix_len];
1228 char *cp = fullname;
1229 Elf_Scn *scn = NULL;
1230 int any = 0;
1231 int result = 0;
1232 GElf_Ehdr ehdr_mem;
1233 GElf_Ehdr *ehdr;
1234 Ebl *ebl;
1235
1236 /* Get the backend for this object file type. */
1237 ebl = ebl_openbackend (elf);
1238
1239 /* We need the ELF header in a few places. */
1240 ehdr = gelf_getehdr (elf, &ehdr_mem);
1241 if (ehdr == NULL)
1242 INTERNAL_ERROR (fullname);
1243
1244 /* If we are asked to print the dynamic symbol table and this is
1245 executable or dynamic executable, fail. */
1246 if (symsec_type == SHT_DYNSYM
1247 && ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
1248 {
1249 /* XXX Add machine specific object file types. */
1250 error (0, 0, gettext ("%s%s%s%s: Invalid operation"),
1251 prefix ?: "", prefix ? "(" : "", fname, prefix ? ")" : "");
1252 result = 1;
1253 goto out;
1254 }
1255
1256 /* Create the full name of the file. */
1257 if (prefix != NULL)
1258 cp = mempcpy (cp, prefix, prefix_len);
1259 cp = mempcpy (cp, fname, fname_len);
1260 if (suffix != NULL)
1261 memcpy (cp - 1, suffix, suffix_len + 1);
1262
1263 /* Find the symbol table.
1264
1265 XXX Can there be more than one? Do we print all? Currently we do. */
1266 while ((scn = elf_nextscn (elf, scn)) != NULL)
1267 {
1268 GElf_Shdr shdr_mem;
1269 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
1270
1271 if (shdr == NULL)
1272 INTERNAL_ERROR (fullname);
1273
1274 if (shdr->sh_type == symsec_type)
1275 {
1276 Elf_Scn *xndxscn = NULL;
1277
1278 /* We have a symbol table. First make sure we remember this. */
1279 any = 1;
1280
1281 /* Look for an extended section index table for this section. */
1282 if (symsec_type == SHT_SYMTAB)
1283 {
1284 size_t scnndx = elf_ndxscn (scn);
1285
1286 while ((xndxscn = elf_nextscn (elf, xndxscn)) != NULL)
1287 {
1288 GElf_Shdr xndxshdr_mem;
1289 GElf_Shdr *xndxshdr = gelf_getshdr (xndxscn, &xndxshdr_mem);
1290
1291 if (xndxshdr == NULL)
1292 INTERNAL_ERROR (fullname);
1293
1294 if (xndxshdr->sh_type == SHT_SYMTAB_SHNDX
1295 && xndxshdr->sh_link == scnndx)
1296 break;
1297 }
1298 }
1299
1300 show_symbols (ebl, ehdr, scn, xndxscn, shdr, prefix, fname,
1301 fullname);
1302 }
1303 }
1304
1305 if (! any)
1306 {
1307 error (0, 0, gettext ("%s%s%s: no symbols"),
1308 prefix ?: "", prefix ? ":" : "", fname);
1309 result = 1;
1310 }
1311
1312 out:
1313 /* Close the ELF backend library descriptor. */
1314 ebl_closebackend (ebl);
1315
1316 return result;
1317}
Ulrich Drepper3cbdd382008-01-02 17:44:39 +00001318
1319
1320#include "debugpred.h"