1 /* Print size information from ELF file.
2 Copyright (C) 2000-2007,2009,2012 Red Hat, Inc.
3 This file is part of Red Hat elfutils.
4 Written by Ulrich Drepper <drepper@redhat.com>, 2000.
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.
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.
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.
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>. */
42 #include <stdio_ext.h>
46 #include <sys/param.h>
51 /* Name and version of program. */
52 static void print_version (FILE *stream, struct argp_state *state);
53 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
55 /* Bug report address. */
56 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
59 /* Values for the parameters which have no short form. */
60 #define OPT_FORMAT 0x100
61 #define OPT_RADIX 0x101
63 /* Definitions of arguments for argp functions. */
64 static const struct argp_option options[] =
66 { NULL, 0, NULL, 0, N_("Output format:"), 0 },
67 { "format", OPT_FORMAT, "FORMAT", 0,
68 N_("Use the output format FORMAT. FORMAT can be `bsd' or `sysv'. "
69 "The default is `bsd'"), 0 },
70 { NULL, 'A', NULL, 0, N_("Same as `--format=sysv'"), 0 },
71 { NULL, 'B', NULL, 0, N_("Same as `--format=bsd'"), 0 },
72 { "radix", OPT_RADIX, "RADIX", 0, N_("Use RADIX for printing symbol values"),
74 { NULL, 'd', NULL, 0, N_("Same as `--radix=10'"), 0 },
75 { NULL, 'o', NULL, 0, N_("Same as `--radix=8'"), 0 },
76 { NULL, 'x', NULL, 0, N_("Same as `--radix=16'"), 0 },
78 N_("Similar to `--format=sysv' output but in one line"), 0 },
80 { NULL, 0, NULL, 0, N_("Output options:"), 0 },
82 N_("Print size and permission flags for loadable segments"), 0 },
83 { "totals", 't', NULL, 0, N_("Display the total sizes (bsd only)"), 0 },
84 { NULL, 0, NULL, 0, NULL, 0 }
87 /* Short description of program. */
88 static const char doc[] = N_("\
89 List section sizes of FILEs (a.out by default).");
91 /* Strings for arguments in help texts. */
92 static const char args_doc[] = N_("[FILE...]");
94 /* Prototype for option handler. */
95 static error_t parse_opt (int key, char *arg, struct argp_state *state);
97 /* Data structure to communicate with argp functions. */
98 static struct argp argp =
100 options, parse_opt, args_doc, doc, NULL, NULL, NULL
104 /* Print symbols in file named FNAME. */
105 static int process_file (const char *fname);
107 /* Handle content of archive. */
108 static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname);
110 /* Handle ELF file. */
111 static void handle_elf (Elf *elf, const char *fullname, const char *fname);
113 /* Show total size. */
114 static void show_bsd_totals (void);
116 #define INTERNAL_ERROR(fname) \
117 error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"), \
118 fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1))
121 /* User-selectable options. */
123 /* The selected output format. */
128 format_sysv_one_line,
132 /* Radix for printed numbers. */
141 /* Mapping of radix and binary class to length. */
142 static const int length_map[2][3] =
147 [radix_decimal] = 10,
153 [radix_decimal] = 20,
158 /* True if total sizes should be printed. */
160 /* To print the total sizes in a reasonable format remember the higest
161 "class" of ELF binaries processed. */
162 static int totals_class;
166 main (int argc, char *argv[])
171 /* Make memory leak detection possible. */
174 /* We use no threads here which can interfere with handling a stream. */
175 __fsetlocking (stdin, FSETLOCKING_BYCALLER);
176 __fsetlocking (stdout, FSETLOCKING_BYCALLER);
177 __fsetlocking (stderr, FSETLOCKING_BYCALLER);
180 setlocale (LC_ALL, "");
182 /* Make sure the message catalog can be found. */
183 bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
185 /* Initialize the message catalog. */
186 textdomain (PACKAGE_TARNAME);
188 /* Parse and process arguments. */
189 argp_parse (&argp, argc, argv, 0, &remaining, NULL);
192 /* Tell the library which version we are expecting. */
193 elf_version (EV_CURRENT);
195 if (remaining == argc)
196 /* The user didn't specify a name so we use a.out. */
197 result = process_file ("a.out");
199 /* Process all the remaining files. */
201 result |= process_file (argv[remaining]);
202 while (++remaining < argc);
204 /* Print the total sizes but only if the output format is BSD and at
205 least one file has been correctly read (i.e., we recognized the
207 if (totals && format == format_bsd && totals_class != 0)
214 /* Print the version information. */
216 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
218 fprintf (stream, "size (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
219 fprintf (stream, gettext ("\
220 Copyright (C) %s Red Hat, Inc.\n\
221 This is free software; see the source for copying conditions. There is NO\n\
222 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
224 fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
228 /* Handle program arguments. */
230 parse_opt (int key, char *arg,
231 struct argp_state *state __attribute__ ((unused)))
236 radix = radix_decimal;
240 format = format_sysv_one_line;
252 format = format_sysv;
260 format = format_segments;
264 if (strcmp (arg, "bsd") == 0 || strcmp (arg, "berkeley") == 0)
266 else if (likely (strcmp (arg, "sysv") == 0))
267 format = format_sysv;
269 error (EXIT_FAILURE, 0, gettext ("Invalid format: %s"), arg);
273 if (strcmp (arg, "x") == 0 || strcmp (arg, "16") == 0)
275 else if (strcmp (arg, "d") == 0 || strcmp (arg, "10") == 0)
276 radix = radix_decimal;
277 else if (strcmp (arg, "o") == 0 || strcmp (arg, "8") == 0)
280 error (EXIT_FAILURE, 0, gettext ("Invalid radix: %s"), arg);
288 return ARGP_ERR_UNKNOWN;
294 /* Open the file and determine the type. */
296 process_file (const char *fname)
298 int fd = open (fname, O_RDONLY);
299 if (unlikely (fd == -1))
301 error (0, errno, gettext ("cannot open '%s'"), fname);
305 /* Now get the ELF descriptor. */
306 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
307 if (likely (elf != NULL))
309 if (elf_kind (elf) == ELF_K_ELF)
311 handle_elf (elf, NULL, fname);
313 if (unlikely (elf_end (elf) != 0))
314 INTERNAL_ERROR (fname);
316 if (unlikely (close (fd) != 0))
317 error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
321 else if (likely (elf_kind (elf) == ELF_K_AR))
323 int result = handle_ar (fd, elf, NULL, fname);
325 if (unlikely (close (fd) != 0))
326 error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
331 /* We cannot handle this type. Close the descriptor anyway. */
332 if (unlikely (elf_end (elf) != 0))
333 INTERNAL_ERROR (fname);
336 if (unlikely (close (fd) != 0))
337 error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
339 error (0, 0, gettext ("%s: file format not recognized"), fname);
345 /* Print the BSD-style header. This is done exactly once. */
347 print_header (Elf *elf)
353 int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal];
354 int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex];
356 printf ("%*s %*s %*s %*s %*s %s\n",
357 ddigits - 2, sgettext ("bsd|text"),
358 ddigits - 2, sgettext ("bsd|data"),
359 ddigits - 2, sgettext ("bsd|bss"),
360 ddigits - 2, sgettext ("bsd|dec"),
361 xdigits - 2, sgettext ("bsd|hex"),
362 sgettext ("bsd|filename"));
370 handle_ar (int fd, Elf *elf, const char *prefix, const char *fname)
372 size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
373 size_t fname_len = strlen (fname) + 1;
374 char new_prefix[prefix_len + 1 + fname_len];
375 char *cp = new_prefix;
377 /* Create the full name of the file. */
380 cp = mempcpy (cp, prefix, prefix_len);
383 memcpy (cp, fname, fname_len);
385 /* Process all the files contained in the archive. */
388 Elf_Cmd cmd = ELF_C_READ_MMAP;
389 while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
391 /* The the header for this element. */
392 Elf_Arhdr *arhdr = elf_getarhdr (subelf);
394 if (elf_kind (subelf) == ELF_K_ELF)
395 handle_elf (subelf, new_prefix, arhdr->ar_name);
396 else if (likely (elf_kind (subelf) == ELF_K_AR))
397 result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name);
398 /* else signal error??? */
400 /* Get next archive element. */
401 cmd = elf_next (subelf);
402 if (unlikely (elf_end (subelf) != 0))
403 INTERNAL_ERROR (fname);
406 if (unlikely (elf_end (elf) != 0))
407 INTERNAL_ERROR (fname);
413 /* Show sizes in SysV format. */
415 show_sysv (Elf *elf, const char *prefix, const char *fname,
416 const char *fullname)
419 const int digits = length_map[gelf_getclass (elf) - 1][radix];
421 /* Get the section header string table index. */
423 if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
424 error (EXIT_FAILURE, 0,
425 gettext ("cannot get section header string table index"));
427 /* First round over the sections: determine the longest section name. */
429 while ((scn = elf_nextscn (elf, scn)) != NULL)
432 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
435 INTERNAL_ERROR (fullname);
437 /* Ignore all sections which are not used at runtime. */
438 if ((shdr->sh_flags & SHF_ALLOC) != 0)
439 maxlen = MAX (maxlen,
440 (int) strlen (elf_strptr (elf, shstrndx,
444 fputs_unlocked (fname, stdout);
446 printf (gettext (" (ex %s)"), prefix);
447 printf (":\n%-*s %*s %*s\n",
448 maxlen, sgettext ("sysv|section"),
449 digits - 2, sgettext ("sysv|size"),
450 digits, sgettext ("sysv|addr"));
453 if (radix == radix_hex)
454 fmtstr = "%-*s %*" PRIx64 " %*" PRIx64 "\n";
455 else if (radix == radix_decimal)
456 fmtstr = "%-*s %*" PRId64 " %*" PRId64 "\n";
458 fmtstr = "%-*s %*" PRIo64 " %*" PRIo64 "\n";
460 /* Iterate over all sections. */
462 while ((scn = elf_nextscn (elf, scn)) != NULL)
465 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
467 /* Ignore all sections which are not used at runtime. */
468 if ((shdr->sh_flags & SHF_ALLOC) != 0)
471 maxlen, elf_strptr (elf, shstrndx, shdr->sh_name),
472 digits - 2, shdr->sh_size,
473 digits, shdr->sh_addr);
475 total += shdr->sh_size;
479 if (radix == radix_hex)
480 printf ("%-*s %*" PRIx64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
482 else if (radix == radix_decimal)
483 printf ("%-*s %*" PRId64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
486 printf ("%-*s %*" PRIo64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
491 /* Show sizes in SysV format in one line. */
493 show_sysv_one_line (Elf *elf)
495 /* Get the section header string table index. */
497 if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
498 error (EXIT_FAILURE, 0,
499 gettext ("cannot get section header string table index"));
502 if (radix == radix_hex)
503 fmtstr = "%" PRIx64 "(%s)";
504 else if (radix == radix_decimal)
505 fmtstr = "%" PRId64 "(%s)";
507 fmtstr = "%" PRIo64 "(%s)";
509 /* Iterate over all sections. */
513 while ((scn = elf_nextscn (elf, scn)) != NULL)
516 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
518 /* Ignore all sections which are not used at runtime. */
519 if ((shdr->sh_flags & SHF_ALLOC) == 0)
523 fputs_unlocked (" + ", stdout);
526 printf (fmtstr, shdr->sh_size,
527 elf_strptr (elf, shstrndx, shdr->sh_name));
529 total += shdr->sh_size;
532 if (radix == radix_hex)
533 printf (" = %#" PRIx64 "\n", total);
534 else if (radix == radix_decimal)
535 printf (" = %" PRId64 "\n", total);
537 printf (" = %" PRIo64 "\n", total);
541 /* Variables to add up the sizes of all files. */
542 static uintmax_t total_textsize;
543 static uintmax_t total_datasize;
544 static uintmax_t total_bsssize;
547 /* Show sizes in BSD format. */
549 show_bsd (Elf *elf, const char *prefix, const char *fname,
550 const char *fullname)
552 GElf_Off textsize = 0;
553 GElf_Off datasize = 0;
554 GElf_Off bsssize = 0;
555 const int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal];
556 const int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex];
558 /* Iterate over all sections. */
560 while ((scn = elf_nextscn (elf, scn)) != NULL)
563 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
566 INTERNAL_ERROR (fullname);
568 /* Ignore all sections which are not marked as loaded. */
569 if ((shdr->sh_flags & SHF_ALLOC) == 0)
572 if ((shdr->sh_flags & SHF_WRITE) == 0)
573 textsize += shdr->sh_size;
574 else if (shdr->sh_type == SHT_NOBITS)
575 bsssize += shdr->sh_size;
577 datasize += shdr->sh_size;
580 printf ("%*" PRId64 " %*" PRId64 " %*" PRId64 " %*" PRId64 " %*"
582 ddigits - 2, textsize,
583 ddigits - 2, datasize,
584 ddigits - 2, bsssize,
585 ddigits - 2, textsize + datasize + bsssize,
586 xdigits - 2, textsize + datasize + bsssize,
589 printf (gettext (" (ex %s)"), prefix);
590 fputs_unlocked ("\n", stdout);
592 total_textsize += textsize;
593 total_datasize += datasize;
594 total_bsssize += bsssize;
596 totals_class = MAX (totals_class, gelf_getclass (elf));
600 /* Show total size. */
602 show_bsd_totals (void)
604 int ddigits = length_map[totals_class - 1][radix_decimal];
605 int xdigits = length_map[totals_class - 1][radix_hex];
607 printf ("%*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*"
609 ddigits - 2, total_textsize,
610 ddigits - 2, total_datasize,
611 ddigits - 2, total_bsssize,
612 ddigits - 2, total_textsize + total_datasize + total_bsssize,
613 xdigits - 2, total_textsize + total_datasize + total_bsssize,
614 gettext ("(TOTALS)\n"));
618 /* Show size and permission of loadable segments. */
620 show_segments (Elf *elf, const char *fullname)
623 GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
625 INTERNAL_ERROR (fullname);
629 for (size_t cnt = 0; cnt < ehdr->e_phnum; ++cnt)
634 phdr = gelf_getphdr (elf, cnt, &phdr_mem);
636 INTERNAL_ERROR (fullname);
638 if (phdr->p_type != PT_LOAD)
639 /* Only load segments. */
643 fputs_unlocked (" + ", stdout);
646 printf (radix == radix_hex ? "%" PRIx64 "(%c%c%c)"
647 : (radix == radix_decimal ? "%" PRId64 "(%c%c%c)"
648 : "%" PRIo64 "(%c%c%c)"),
650 (phdr->p_flags & PF_R) == 0 ? '-' : 'r',
651 (phdr->p_flags & PF_W) == 0 ? '-' : 'w',
652 (phdr->p_flags & PF_X) == 0 ? '-' : 'x');
654 total += phdr->p_memsz;
657 if (radix == radix_hex)
658 printf (" = %#" PRIx64 "\n", total);
659 else if (radix == radix_decimal)
660 printf (" = %" PRId64 "\n", total);
662 printf (" = %" PRIo64 "\n", total);
667 handle_elf (Elf *elf, const char *prefix, const char *fname)
669 size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
670 size_t fname_len = strlen (fname) + 1;
671 char fullname[prefix_len + 1 + fname_len];
674 /* Create the full name of the file. */
677 cp = mempcpy (cp, prefix, prefix_len);
680 memcpy (cp, fname, fname_len);
682 if (format == format_sysv)
683 show_sysv (elf, prefix, fname, fullname);
684 else if (format == format_sysv_one_line)
685 show_sysv_one_line (elf);
686 else if (format == format_segments)
687 show_segments (elf, fullname);
692 show_bsd (elf, prefix, fname, fullname);
697 #include "debugpred.h"