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