Imported Upstream version 0.160
[platform/upstream/elfutils.git] / src / size.c
1 /* Print size information from ELF file.
2    Copyright (C) 2000-2007,2009,2012,2014 Red Hat, Inc.
3    This file is part of elfutils.
4    Written by Ulrich Drepper <drepper@redhat.com>, 2000.
5
6    This file is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    elfutils is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <argp.h>
24 #include <error.h>
25 #include <fcntl.h>
26 #include <gelf.h>
27 #include <inttypes.h>
28 #include <libelf.h>
29 #include <libintl.h>
30 #include <locale.h>
31 #include <mcheck.h>
32 #include <stdbool.h>
33 #include <stdio.h>
34 #include <stdio_ext.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include <sys/param.h>
39
40 #include <system.h>
41
42
43 /* Name and version of program.  */
44 static void print_version (FILE *stream, struct argp_state *state);
45 ARGP_PROGRAM_VERSION_HOOK_DEF = print_version;
46
47 /* Bug report address.  */
48 ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT;
49
50
51 /* Values for the parameters which have no short form.  */
52 #define OPT_FORMAT      0x100
53 #define OPT_RADIX       0x101
54
55 /* Definitions of arguments for argp functions.  */
56 static const struct argp_option options[] =
57 {
58   { NULL, 0, NULL, 0, N_("Output format:"), 0 },
59   { "format", OPT_FORMAT, "FORMAT", 0,
60     N_("Use the output format FORMAT.  FORMAT can be `bsd' or `sysv'.  "
61        "The default is `bsd'"), 0 },
62   { NULL, 'A', NULL, 0, N_("Same as `--format=sysv'"), 0 },
63   { NULL, 'B', NULL, 0, N_("Same as `--format=bsd'"), 0 },
64   { "radix", OPT_RADIX, "RADIX", 0, N_("Use RADIX for printing symbol values"),
65     0},
66   { NULL, 'd', NULL, 0, N_("Same as `--radix=10'"), 0 },
67   { NULL, 'o', NULL, 0, N_("Same as `--radix=8'"), 0 },
68   { NULL, 'x', NULL, 0, N_("Same as `--radix=16'"), 0 },
69   { NULL, 'f', NULL, 0,
70     N_("Similar to `--format=sysv' output but in one line"), 0 },
71
72   { NULL, 0, NULL, 0, N_("Output options:"), 0 },
73   { NULL, 'F', NULL, 0,
74     N_("Print size and permission flags for loadable segments"), 0 },
75   { "totals", 't', NULL, 0, N_("Display the total sizes (bsd only)"), 0 },
76   { NULL, 0, NULL, 0, NULL, 0 }
77 };
78
79 /* Short description of program.  */
80 static const char doc[] = N_("\
81 List section sizes of FILEs (a.out by default).");
82
83 /* Strings for arguments in help texts.  */
84 static const char args_doc[] = N_("[FILE...]");
85
86 /* Prototype for option handler.  */
87 static error_t parse_opt (int key, char *arg, struct argp_state *state);
88
89 /* Data structure to communicate with argp functions.  */
90 static struct argp argp =
91 {
92   options, parse_opt, args_doc, doc, NULL, NULL, NULL
93 };
94
95
96 /* Print symbols in file named FNAME.  */
97 static int process_file (const char *fname);
98
99 /* Handle content of archive.  */
100 static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname);
101
102 /* Handle ELF file.  */
103 static void handle_elf (Elf *elf, const char *fullname, const char *fname);
104
105 /* Show total size.  */
106 static void show_bsd_totals (void);
107
108 #define INTERNAL_ERROR(fname) \
109   error (EXIT_FAILURE, 0, gettext ("%s: INTERNAL ERROR %d (%s-%s): %s"),      \
110          fname, __LINE__, PACKAGE_VERSION, __DATE__, elf_errmsg (-1))
111
112
113 /* User-selectable options.  */
114
115 /* The selected output format.  */
116 static enum
117 {
118   format_bsd = 0,
119   format_sysv,
120   format_sysv_one_line,
121   format_segments
122 } format;
123
124 /* Radix for printed numbers.  */
125 static enum
126 {
127   radix_decimal = 0,
128   radix_hex,
129   radix_octal
130 } radix;
131
132
133 /* Mapping of radix and binary class to length.  */
134 static const int length_map[2][3] =
135 {
136   [ELFCLASS32 - 1] =
137   {
138     [radix_hex] = 8,
139     [radix_decimal] = 10,
140     [radix_octal] = 11
141   },
142   [ELFCLASS64 - 1] =
143   {
144     [radix_hex] = 16,
145     [radix_decimal] = 20,
146     [radix_octal] = 22
147   }
148 };
149
150 /* True if total sizes should be printed.  */
151 static bool totals;
152 /* To print the total sizes in a reasonable format remember the higest
153    "class" of ELF binaries processed.  */
154 static int totals_class;
155
156
157 int
158 main (int argc, char *argv[])
159 {
160   int remaining;
161   int result = 0;
162
163   /* Make memory leak detection possible.  */
164   mtrace ();
165
166   /* We use no threads here which can interfere with handling a stream.  */
167   __fsetlocking (stdin, FSETLOCKING_BYCALLER);
168   __fsetlocking (stdout, FSETLOCKING_BYCALLER);
169   __fsetlocking (stderr, FSETLOCKING_BYCALLER);
170
171   /* Set locale.  */
172   setlocale (LC_ALL, "");
173
174   /* Make sure the message catalog can be found.  */
175   bindtextdomain (PACKAGE_TARNAME, LOCALEDIR);
176
177   /* Initialize the message catalog.  */
178   textdomain (PACKAGE_TARNAME);
179
180   /* Parse and process arguments.  */
181   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
182
183
184   /* Tell the library which version we are expecting.  */
185   elf_version (EV_CURRENT);
186
187   if (remaining == argc)
188     /* The user didn't specify a name so we use a.out.  */
189     result = process_file ("a.out");
190   else
191     /* Process all the remaining files.  */
192     do
193       result |= process_file (argv[remaining]);
194     while (++remaining < argc);
195
196   /* Print the total sizes but only if the output format is BSD and at
197      least one file has been correctly read (i.e., we recognized the
198      class).  */
199   if (totals && format == format_bsd && totals_class != 0)
200     show_bsd_totals ();
201
202   return result;
203 }
204
205
206 /* Print the version information.  */
207 static void
208 print_version (FILE *stream, struct argp_state *state __attribute__ ((unused)))
209 {
210   fprintf (stream, "size (%s) %s\n", PACKAGE_NAME, PACKAGE_VERSION);
211   fprintf (stream, gettext ("\
212 Copyright (C) %s Red Hat, Inc.\n\
213 This is free software; see the source for copying conditions.  There is NO\n\
214 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n\
215 "), "2012");
216   fprintf (stream, gettext ("Written by %s.\n"), "Ulrich Drepper");
217 }
218
219
220 /* Handle program arguments.  */
221 static error_t
222 parse_opt (int key, char *arg,
223            struct argp_state *state __attribute__ ((unused)))
224 {
225   switch (key)
226     {
227     case 'd':
228       radix = radix_decimal;
229       break;
230
231     case 'f':
232       format = format_sysv_one_line;
233       break;
234
235     case 'o':
236       radix = radix_octal;
237       break;
238
239     case 'x':
240       radix = radix_hex;
241       break;
242
243     case 'A':
244       format = format_sysv;
245       break;
246
247     case 'B':
248       format = format_bsd;
249       break;
250
251     case 'F':
252       format = format_segments;
253       break;
254
255     case OPT_FORMAT:
256       if (strcmp (arg, "bsd") == 0 || strcmp (arg, "berkeley") == 0)
257         format = format_bsd;
258       else if (likely (strcmp (arg, "sysv") == 0))
259         format = format_sysv;
260       else
261         error (EXIT_FAILURE, 0, gettext ("Invalid format: %s"), arg);
262       break;
263
264     case OPT_RADIX:
265       if (strcmp (arg, "x") == 0 || strcmp (arg, "16") == 0)
266         radix = radix_hex;
267       else if (strcmp (arg, "d") == 0 || strcmp (arg, "10") == 0)
268         radix = radix_decimal;
269       else if (strcmp (arg, "o") == 0 || strcmp (arg, "8") == 0)
270         radix = radix_octal;
271       else
272         error (EXIT_FAILURE, 0, gettext ("Invalid radix: %s"), arg);
273       break;
274
275     case 't':
276       totals = true;
277       break;
278
279     default:
280       return ARGP_ERR_UNKNOWN;
281     }
282   return 0;
283 }
284
285
286 /* Open the file and determine the type.  */
287 static int
288 process_file (const char *fname)
289 {
290   int fd = open (fname, O_RDONLY);
291   if (unlikely (fd == -1))
292     {
293       error (0, errno, gettext ("cannot open '%s'"), fname);
294       return 1;
295     }
296
297   /* Now get the ELF descriptor.  */
298   Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL);
299   if (likely (elf != NULL))
300     {
301       if (elf_kind (elf) == ELF_K_ELF)
302         {
303           handle_elf (elf, NULL, fname);
304
305           if (unlikely (elf_end (elf) != 0))
306             INTERNAL_ERROR (fname);
307
308           if (unlikely (close (fd) != 0))
309             error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
310
311           return 0;
312         }
313       else if (likely (elf_kind (elf) == ELF_K_AR))
314         {
315           int result = handle_ar (fd, elf, NULL, fname);
316
317           if (unlikely  (close (fd) != 0))
318             error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
319
320           return result;
321         }
322
323       /* We cannot handle this type.  Close the descriptor anyway.  */
324       if (unlikely (elf_end (elf) != 0))
325         INTERNAL_ERROR (fname);
326     }
327
328   if (unlikely (close (fd) != 0))
329     error (EXIT_FAILURE, errno, gettext ("while closing '%s'"), fname);
330
331   error (0, 0, gettext ("%s: file format not recognized"), fname);
332
333   return 1;
334 }
335
336
337 /* Print the BSD-style header.  This is done exactly once.  */
338 static void
339 print_header (Elf *elf)
340 {
341   static int done;
342
343   if (! done)
344     {
345       int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal];
346       int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex];
347
348       printf ("%*s %*s %*s %*s %*s %s\n",
349               ddigits - 2, sgettext ("bsd|text"),
350               ddigits - 2, sgettext ("bsd|data"),
351               ddigits - 2, sgettext ("bsd|bss"),
352               ddigits - 2, sgettext ("bsd|dec"),
353               xdigits - 2, sgettext ("bsd|hex"),
354               sgettext ("bsd|filename"));
355
356       done = 1;
357     }
358 }
359
360
361 static int
362 handle_ar (int fd, Elf *elf, const char *prefix, const char *fname)
363 {
364   size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
365   size_t fname_len = strlen (fname) + 1;
366   char new_prefix[prefix_len + 1 + fname_len];
367   char *cp = new_prefix;
368
369   /* Create the full name of the file.  */
370   if (prefix != NULL)
371     {
372       cp = mempcpy (cp, prefix, prefix_len);
373       *cp++ = ':';
374     }
375   memcpy (cp, fname, fname_len);
376
377   /* Process all the files contained in the archive.  */
378   int result = 0;
379   Elf *subelf;
380   Elf_Cmd cmd = ELF_C_READ_MMAP;
381   while ((subelf = elf_begin (fd, cmd, elf)) != NULL)
382     {
383       /* The the header for this element.  */
384       Elf_Arhdr *arhdr = elf_getarhdr (subelf);
385
386       if (elf_kind (subelf) == ELF_K_ELF)
387         handle_elf (subelf, new_prefix, arhdr->ar_name);
388       else if (likely (elf_kind (subelf) == ELF_K_AR))
389         result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name);
390       /* else signal error??? */
391
392       /* Get next archive element.  */
393       cmd = elf_next (subelf);
394       if (unlikely (elf_end (subelf) != 0))
395         INTERNAL_ERROR (fname);
396     }
397
398   if (unlikely (elf_end (elf) != 0))
399     INTERNAL_ERROR (fname);
400
401   return result;
402 }
403
404
405 /* Show sizes in SysV format.  */
406 static void
407 show_sysv (Elf *elf, const char *prefix, const char *fname,
408            const char *fullname)
409 {
410   int maxlen = 10;
411   const int digits = length_map[gelf_getclass (elf) - 1][radix];
412
413   /* Get the section header string table index.  */
414   size_t shstrndx;
415   if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
416     error (EXIT_FAILURE, 0,
417            gettext ("cannot get section header string table index"));
418
419   /* First round over the sections: determine the longest section name.  */
420   Elf_Scn *scn = NULL;
421   while ((scn = elf_nextscn (elf, scn)) != NULL)
422     {
423       GElf_Shdr shdr_mem;
424       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
425
426       if (shdr == NULL)
427         INTERNAL_ERROR (fullname);
428
429       /* Ignore all sections which are not used at runtime.  */
430       if ((shdr->sh_flags & SHF_ALLOC) != 0)
431         maxlen = MAX (maxlen,
432                       (int) strlen (elf_strptr (elf, shstrndx,
433                                                 shdr->sh_name)));
434     }
435
436   fputs_unlocked (fname, stdout);
437   if (prefix != NULL)
438     printf (gettext (" (ex %s)"), prefix);
439   printf (":\n%-*s %*s %*s\n",
440           maxlen, sgettext ("sysv|section"),
441           digits - 2, sgettext ("sysv|size"),
442           digits, sgettext ("sysv|addr"));
443
444   /* Iterate over all sections.  */
445   GElf_Off total = 0;
446   while ((scn = elf_nextscn (elf, scn)) != NULL)
447     {
448       GElf_Shdr shdr_mem;
449       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
450
451       /* Ignore all sections which are not used at runtime.  */
452       if ((shdr->sh_flags & SHF_ALLOC) != 0)
453         {
454           printf ((radix == radix_hex
455                    ? "%-*s %*" PRIx64 " %*" PRIx64 "\n"
456                    : (radix == radix_decimal
457                       ? "%-*s %*" PRId64 " %*" PRId64 "\n"
458                       : "%-*s %*" PRIo64 " %*" PRIo64 "\n")),
459                   maxlen, elf_strptr (elf, shstrndx, shdr->sh_name),
460                   digits - 2, shdr->sh_size,
461                   digits, shdr->sh_addr);
462
463           total += shdr->sh_size;
464         }
465     }
466
467   if (radix == radix_hex)
468     printf ("%-*s %*" PRIx64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
469             digits - 2, total);
470   else if (radix == radix_decimal)
471     printf ("%-*s %*" PRId64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
472             digits - 2, total);
473   else
474     printf ("%-*s %*" PRIo64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
475             digits - 2, total);
476 }
477
478
479 /* Show sizes in SysV format in one line.  */
480 static void
481 show_sysv_one_line (Elf *elf)
482 {
483   /* Get the section header string table index.  */
484   size_t shstrndx;
485   if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
486     error (EXIT_FAILURE, 0,
487            gettext ("cannot get section header string table index"));
488
489   /* Iterate over all sections.  */
490   GElf_Off total = 0;
491   bool first = true;
492   Elf_Scn *scn = NULL;
493   while ((scn = elf_nextscn (elf, scn)) != NULL)
494     {
495       GElf_Shdr shdr_mem;
496       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
497
498       /* Ignore all sections which are not used at runtime.  */
499       if ((shdr->sh_flags & SHF_ALLOC) == 0)
500         continue;
501
502       if (! first)
503         fputs_unlocked (" + ", stdout);
504       first = false;
505
506       printf ((radix == radix_hex ? "%" PRIx64 "(%s)"
507                : (radix == radix_decimal ? "%" PRId64 "(%s)"
508                   : "%" PRIo64 "(%s)")),
509               shdr->sh_size, elf_strptr (elf, shstrndx, shdr->sh_name));
510
511       total += shdr->sh_size;
512     }
513
514   if (radix == radix_hex)
515     printf (" = %#" PRIx64 "\n", total);
516   else if (radix == radix_decimal)
517     printf (" = %" PRId64 "\n", total);
518   else
519     printf (" = %" PRIo64 "\n", total);
520 }
521
522
523 /* Variables to add up the sizes of all files.  */
524 static uintmax_t total_textsize;
525 static uintmax_t total_datasize;
526 static uintmax_t total_bsssize;
527
528
529 /* Show sizes in BSD format.  */
530 static void
531 show_bsd (Elf *elf, const char *prefix, const char *fname,
532           const char *fullname)
533 {
534   GElf_Off textsize = 0;
535   GElf_Off datasize = 0;
536   GElf_Off bsssize = 0;
537   const int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal];
538   const int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex];
539
540   /* Iterate over all sections.  */
541   Elf_Scn *scn = NULL;
542   while ((scn = elf_nextscn (elf, scn)) != NULL)
543     {
544       GElf_Shdr shdr_mem;
545       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
546
547       if (shdr == NULL)
548         INTERNAL_ERROR (fullname);
549
550       /* Ignore all sections which are not marked as loaded.  */
551       if ((shdr->sh_flags & SHF_ALLOC) == 0)
552         continue;
553
554       if ((shdr->sh_flags & SHF_WRITE) == 0)
555         textsize += shdr->sh_size;
556       else if (shdr->sh_type == SHT_NOBITS)
557         bsssize += shdr->sh_size;
558       else
559         datasize += shdr->sh_size;
560     }
561
562   printf ("%*" PRId64 " %*" PRId64 " %*" PRId64 " %*" PRId64 " %*"
563           PRIx64 " %s",
564           ddigits - 2, textsize,
565           ddigits - 2, datasize,
566           ddigits - 2, bsssize,
567           ddigits - 2, textsize + datasize + bsssize,
568           xdigits - 2, textsize + datasize + bsssize,
569           fname);
570   if (prefix != NULL)
571     printf (gettext (" (ex %s)"), prefix);
572   fputs_unlocked ("\n", stdout);
573
574   total_textsize += textsize;
575   total_datasize += datasize;
576   total_bsssize += bsssize;
577
578   totals_class = MAX (totals_class, gelf_getclass (elf));
579 }
580
581
582 /* Show total size.  */
583 static void
584 show_bsd_totals (void)
585 {
586   int ddigits = length_map[totals_class - 1][radix_decimal];
587   int xdigits = length_map[totals_class - 1][radix_hex];
588
589   printf ("%*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*"
590           PRIxMAX " %s",
591           ddigits - 2, total_textsize,
592           ddigits - 2, total_datasize,
593           ddigits - 2, total_bsssize,
594           ddigits - 2, total_textsize + total_datasize + total_bsssize,
595           xdigits - 2, total_textsize + total_datasize + total_bsssize,
596           gettext ("(TOTALS)\n"));
597 }
598
599
600 /* Show size and permission of loadable segments.  */
601 static void
602 show_segments (Elf *elf, const char *fullname)
603 {
604   GElf_Ehdr ehdr_mem;
605   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
606   if (ehdr == NULL)
607     INTERNAL_ERROR (fullname);
608
609   GElf_Off total = 0;
610   bool first = true;
611   for (size_t cnt = 0; cnt < ehdr->e_phnum; ++cnt)
612     {
613       GElf_Phdr phdr_mem;
614       GElf_Phdr *phdr;
615
616       phdr = gelf_getphdr (elf, cnt, &phdr_mem);
617       if (phdr == NULL)
618         INTERNAL_ERROR (fullname);
619
620       if (phdr->p_type != PT_LOAD)
621         /* Only load segments.  */
622         continue;
623
624       if (! first)
625         fputs_unlocked (" + ", stdout);
626       first = false;
627
628       printf (radix == radix_hex ? "%" PRIx64 "(%c%c%c)"
629               : (radix == radix_decimal ? "%" PRId64 "(%c%c%c)"
630                  : "%" PRIo64 "(%c%c%c)"),
631               phdr->p_memsz,
632               (phdr->p_flags & PF_R) == 0 ? '-' : 'r',
633               (phdr->p_flags & PF_W) == 0 ? '-' : 'w',
634               (phdr->p_flags & PF_X) == 0 ? '-' : 'x');
635
636       total += phdr->p_memsz;
637     }
638
639   if (radix == radix_hex)
640     printf (" = %#" PRIx64 "\n", total);
641   else if (radix == radix_decimal)
642     printf (" = %" PRId64 "\n", total);
643   else
644     printf (" = %" PRIo64 "\n", total);
645 }
646
647
648 static void
649 handle_elf (Elf *elf, const char *prefix, const char *fname)
650 {
651   size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
652   size_t fname_len = strlen (fname) + 1;
653   char fullname[prefix_len + 1 + fname_len];
654   char *cp = fullname;
655
656   /* Create the full name of the file.  */
657   if (prefix != NULL)
658     {
659       cp = mempcpy (cp, prefix, prefix_len);
660       *cp++ = ':';
661     }
662   memcpy (cp, fname, fname_len);
663
664   if (format == format_sysv)
665     show_sysv (elf, prefix, fname, fullname);
666   else if (format == format_sysv_one_line)
667     show_sysv_one_line (elf);
668   else if (format == format_segments)
669     show_segments (elf, fullname);
670   else
671     {
672       print_header (elf);
673
674       show_bsd (elf, prefix, fname, fullname);
675     }
676 }
677
678
679 #include "debugpred.h"