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