Imported Upstream version 0.155
[platform/upstream/elfutils.git] / src / size.c
1 /* Print size information from ELF file.
2    Copyright (C) 2000-2007,2009,2012 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   const char *fmtstr;
445   if (radix == radix_hex)
446     fmtstr = "%-*s %*" PRIx64 " %*" PRIx64 "\n";
447   else if (radix == radix_decimal)
448     fmtstr = "%-*s %*" PRId64 " %*" PRId64 "\n";
449   else
450     fmtstr = "%-*s %*" PRIo64 " %*" PRIo64 "\n";
451
452   /* Iterate over all sections.  */
453   GElf_Off total = 0;
454   while ((scn = elf_nextscn (elf, scn)) != NULL)
455     {
456       GElf_Shdr shdr_mem;
457       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
458
459       /* Ignore all sections which are not used at runtime.  */
460       if ((shdr->sh_flags & SHF_ALLOC) != 0)
461         {
462           printf (fmtstr,
463                   maxlen, elf_strptr (elf, shstrndx, shdr->sh_name),
464                   digits - 2, shdr->sh_size,
465                   digits, shdr->sh_addr);
466
467           total += shdr->sh_size;
468         }
469     }
470
471   if (radix == radix_hex)
472     printf ("%-*s %*" PRIx64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
473             digits - 2, total);
474   else if (radix == radix_decimal)
475     printf ("%-*s %*" PRId64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
476             digits - 2, total);
477   else
478     printf ("%-*s %*" PRIo64 "\n\n\n", maxlen, sgettext ("sysv|Total"),
479             digits - 2, total);
480 }
481
482
483 /* Show sizes in SysV format in one line.  */
484 static void
485 show_sysv_one_line (Elf *elf)
486 {
487   /* Get the section header string table index.  */
488   size_t shstrndx;
489   if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0))
490     error (EXIT_FAILURE, 0,
491            gettext ("cannot get section header string table index"));
492
493   const char *fmtstr;
494   if (radix == radix_hex)
495     fmtstr = "%" PRIx64 "(%s)";
496   else if (radix == radix_decimal)
497     fmtstr = "%" PRId64 "(%s)";
498   else
499     fmtstr = "%" PRIo64 "(%s)";
500
501   /* Iterate over all sections.  */
502   GElf_Off total = 0;
503   bool first = true;
504   Elf_Scn *scn = NULL;
505   while ((scn = elf_nextscn (elf, scn)) != NULL)
506     {
507       GElf_Shdr shdr_mem;
508       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
509
510       /* Ignore all sections which are not used at runtime.  */
511       if ((shdr->sh_flags & SHF_ALLOC) == 0)
512         continue;
513
514       if (! first)
515         fputs_unlocked (" + ", stdout);
516       first = false;
517
518       printf (fmtstr, shdr->sh_size,
519               elf_strptr (elf, shstrndx, shdr->sh_name));
520
521       total += shdr->sh_size;
522     }
523
524   if (radix == radix_hex)
525     printf (" = %#" PRIx64 "\n", total);
526   else if (radix == radix_decimal)
527     printf (" = %" PRId64 "\n", total);
528   else
529     printf (" = %" PRIo64 "\n", total);
530 }
531
532
533 /* Variables to add up the sizes of all files.  */
534 static uintmax_t total_textsize;
535 static uintmax_t total_datasize;
536 static uintmax_t total_bsssize;
537
538
539 /* Show sizes in BSD format.  */
540 static void
541 show_bsd (Elf *elf, const char *prefix, const char *fname,
542           const char *fullname)
543 {
544   GElf_Off textsize = 0;
545   GElf_Off datasize = 0;
546   GElf_Off bsssize = 0;
547   const int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal];
548   const int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex];
549
550   /* Iterate over all sections.  */
551   Elf_Scn *scn = NULL;
552   while ((scn = elf_nextscn (elf, scn)) != NULL)
553     {
554       GElf_Shdr shdr_mem;
555       GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
556
557       if (shdr == NULL)
558         INTERNAL_ERROR (fullname);
559
560       /* Ignore all sections which are not marked as loaded.  */
561       if ((shdr->sh_flags & SHF_ALLOC) == 0)
562         continue;
563
564       if ((shdr->sh_flags & SHF_WRITE) == 0)
565         textsize += shdr->sh_size;
566       else if (shdr->sh_type == SHT_NOBITS)
567         bsssize += shdr->sh_size;
568       else
569         datasize += shdr->sh_size;
570     }
571
572   printf ("%*" PRId64 " %*" PRId64 " %*" PRId64 " %*" PRId64 " %*"
573           PRIx64 " %s",
574           ddigits - 2, textsize,
575           ddigits - 2, datasize,
576           ddigits - 2, bsssize,
577           ddigits - 2, textsize + datasize + bsssize,
578           xdigits - 2, textsize + datasize + bsssize,
579           fname);
580   if (prefix != NULL)
581     printf (gettext (" (ex %s)"), prefix);
582   fputs_unlocked ("\n", stdout);
583
584   total_textsize += textsize;
585   total_datasize += datasize;
586   total_bsssize += bsssize;
587
588   totals_class = MAX (totals_class, gelf_getclass (elf));
589 }
590
591
592 /* Show total size.  */
593 static void
594 show_bsd_totals (void)
595 {
596   int ddigits = length_map[totals_class - 1][radix_decimal];
597   int xdigits = length_map[totals_class - 1][radix_hex];
598
599   printf ("%*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*"
600           PRIxMAX " %s",
601           ddigits - 2, total_textsize,
602           ddigits - 2, total_datasize,
603           ddigits - 2, total_bsssize,
604           ddigits - 2, total_textsize + total_datasize + total_bsssize,
605           xdigits - 2, total_textsize + total_datasize + total_bsssize,
606           gettext ("(TOTALS)\n"));
607 }
608
609
610 /* Show size and permission of loadable segments.  */
611 static void
612 show_segments (Elf *elf, const char *fullname)
613 {
614   GElf_Ehdr ehdr_mem;
615   GElf_Ehdr *ehdr = gelf_getehdr (elf, &ehdr_mem);
616   if (ehdr == NULL)
617     INTERNAL_ERROR (fullname);
618
619   GElf_Off total = 0;
620   bool first = true;
621   for (size_t cnt = 0; cnt < ehdr->e_phnum; ++cnt)
622     {
623       GElf_Phdr phdr_mem;
624       GElf_Phdr *phdr;
625
626       phdr = gelf_getphdr (elf, cnt, &phdr_mem);
627       if (phdr == NULL)
628         INTERNAL_ERROR (fullname);
629
630       if (phdr->p_type != PT_LOAD)
631         /* Only load segments.  */
632         continue;
633
634       if (! first)
635         fputs_unlocked (" + ", stdout);
636       first = false;
637
638       printf (radix == radix_hex ? "%" PRIx64 "(%c%c%c)"
639               : (radix == radix_decimal ? "%" PRId64 "(%c%c%c)"
640                  : "%" PRIo64 "(%c%c%c)"),
641               phdr->p_memsz,
642               (phdr->p_flags & PF_R) == 0 ? '-' : 'r',
643               (phdr->p_flags & PF_W) == 0 ? '-' : 'w',
644               (phdr->p_flags & PF_X) == 0 ? '-' : 'x');
645
646       total += phdr->p_memsz;
647     }
648
649   if (radix == radix_hex)
650     printf (" = %#" PRIx64 "\n", total);
651   else if (radix == radix_decimal)
652     printf (" = %" PRId64 "\n", total);
653   else
654     printf (" = %" PRIo64 "\n", total);
655 }
656
657
658 static void
659 handle_elf (Elf *elf, const char *prefix, const char *fname)
660 {
661   size_t prefix_len = prefix == NULL ? 0 : strlen (prefix);
662   size_t fname_len = strlen (fname) + 1;
663   char fullname[prefix_len + 1 + fname_len];
664   char *cp = fullname;
665
666   /* Create the full name of the file.  */
667   if (prefix != NULL)
668     {
669       cp = mempcpy (cp, prefix, prefix_len);
670       *cp++ = ':';
671     }
672   memcpy (cp, fname, fname_len);
673
674   if (format == format_sysv)
675     show_sysv (elf, prefix, fname, fullname);
676   else if (format == format_sysv_one_line)
677     show_sysv_one_line (elf);
678   else if (format == format_segments)
679     show_segments (elf, fullname);
680   else
681     {
682       print_header (elf);
683
684       show_bsd (elf, prefix, fname, fullname);
685     }
686 }
687
688
689 #include "debugpred.h"