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. */ 44static void print_version (FILE *stream, struct argp_state *state); 45ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; 46 47/* Bug report address. */ 48ARGP_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. */ 56static 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. */ 80static const char doc[] = N_("\ 81List section sizes of FILEs (a.out by default)."); 82 83/* Strings for arguments in help texts. */ 84static const char args_doc[] = N_("[FILE...]"); 85 86/* Prototype for option handler. */ 87static error_t parse_opt (int key, char *arg, struct argp_state *state); 88 89/* Data structure to communicate with argp functions. */ 90static struct argp argp = 91{ 92 options, parse_opt, args_doc, doc, NULL, NULL, NULL 93}; 94 95 96/* Print symbols in file named FNAME. */ 97static int process_file (const char *fname); 98 99/* Handle content of archive. */ 100static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname); 101 102/* Handle ELF file. */ 103static void handle_elf (Elf *elf, const char *fullname, const char *fname); 104 105/* Show total size. */ 106static 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. */ 116static 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. */ 125static 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. */ 134static 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. */ 151static bool totals; 152/* To print the total sizes in a reasonable format remember the higest 153 "class" of ELF binaries processed. */ 154static int totals_class; 155 156 157int 158main (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. */ 207static void 208print_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 ("\ 212Copyright (C) %s Red Hat, Inc.\n\ 213This is free software; see the source for copying conditions. There is NO\n\ 214warranty; 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. */ 221static error_t 222parse_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. */ 287static int 288process_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. */ 338static void 339print_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 361static int 362handle_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. */ 406static void 407show_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 const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); 431 if (name != NULL && (shdr->sh_flags & SHF_ALLOC) != 0) 432 maxlen = MAX (maxlen, (int) strlen (name)); 433 } 434 435 fputs_unlocked (fname, stdout); 436 if (prefix != NULL) 437 printf (gettext (" (ex %s)"), prefix); 438 printf (":\n%-*s %*s %*s\n", 439 maxlen, sgettext ("sysv|section"), 440 digits - 2, sgettext ("sysv|size"), 441 digits, sgettext ("sysv|addr")); 442 443 /* Iterate over all sections. */ 444 GElf_Off total = 0; 445 while ((scn = elf_nextscn (elf, scn)) != NULL) 446 { 447 GElf_Shdr shdr_mem; 448 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 449 450 /* Ignore all sections which are not used at runtime. */ 451 if ((shdr->sh_flags & SHF_ALLOC) != 0) 452 { 453 printf ((radix == radix_hex 454 ? "%-*s %*" PRIx64 " %*" PRIx64 "\n" 455 : (radix == radix_decimal 456 ? "%-*s %*" PRId64 " %*" PRId64 "\n" 457 : "%-*s %*" PRIo64 " %*" PRIo64 "\n")), 458 maxlen, elf_strptr (elf, shstrndx, shdr->sh_name), 459 digits - 2, shdr->sh_size, 460 digits, shdr->sh_addr); 461 462 total += shdr->sh_size; 463 } 464 } 465 466 if (radix == radix_hex) 467 printf ("%-*s %*" PRIx64 "\n\n\n", maxlen, sgettext ("sysv|Total"), 468 digits - 2, total); 469 else if (radix == radix_decimal) 470 printf ("%-*s %*" PRId64 "\n\n\n", maxlen, sgettext ("sysv|Total"), 471 digits - 2, total); 472 else 473 printf ("%-*s %*" PRIo64 "\n\n\n", maxlen, sgettext ("sysv|Total"), 474 digits - 2, total); 475} 476 477 478/* Show sizes in SysV format in one line. */ 479static void 480show_sysv_one_line (Elf *elf) 481{ 482 /* Get the section header string table index. */ 483 size_t shstrndx; 484 if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0)) 485 error (EXIT_FAILURE, 0, 486 gettext ("cannot get section header string table index")); 487 488 /* Iterate over all sections. */ 489 GElf_Off total = 0; 490 bool first = true; 491 Elf_Scn *scn = NULL; 492 while ((scn = elf_nextscn (elf, scn)) != NULL) 493 { 494 GElf_Shdr shdr_mem; 495 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 496 497 /* Ignore all sections which are not used at runtime. */ 498 if ((shdr->sh_flags & SHF_ALLOC) == 0) 499 continue; 500 501 if (! first) 502 fputs_unlocked (" + ", stdout); 503 first = false; 504 505 printf ((radix == radix_hex ? "%" PRIx64 "(%s)" 506 : (radix == radix_decimal ? "%" PRId64 "(%s)" 507 : "%" PRIo64 "(%s)")), 508 shdr->sh_size, elf_strptr (elf, shstrndx, shdr->sh_name)); 509 510 total += shdr->sh_size; 511 } 512 513 if (radix == radix_hex) 514 printf (" = %#" PRIx64 "\n", total); 515 else if (radix == radix_decimal) 516 printf (" = %" PRId64 "\n", total); 517 else 518 printf (" = %" PRIo64 "\n", total); 519} 520 521 522/* Variables to add up the sizes of all files. */ 523static uintmax_t total_textsize; 524static uintmax_t total_datasize; 525static uintmax_t total_bsssize; 526 527 528/* Show sizes in BSD format. */ 529static void 530show_bsd (Elf *elf, const char *prefix, const char *fname, 531 const char *fullname) 532{ 533 GElf_Off textsize = 0; 534 GElf_Off datasize = 0; 535 GElf_Off bsssize = 0; 536 const int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal]; 537 const int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex]; 538 539 /* Iterate over all sections. */ 540 Elf_Scn *scn = NULL; 541 while ((scn = elf_nextscn (elf, scn)) != NULL) 542 { 543 GElf_Shdr shdr_mem; 544 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 545 546 if (shdr == NULL) 547 INTERNAL_ERROR (fullname); 548 549 /* Ignore all sections which are not marked as loaded. */ 550 if ((shdr->sh_flags & SHF_ALLOC) == 0) 551 continue; 552 553 if ((shdr->sh_flags & SHF_WRITE) == 0) 554 textsize += shdr->sh_size; 555 else if (shdr->sh_type == SHT_NOBITS) 556 bsssize += shdr->sh_size; 557 else 558 datasize += shdr->sh_size; 559 } 560 561 printf ("%*" PRId64 " %*" PRId64 " %*" PRId64 " %*" PRId64 " %*" 562 PRIx64 " %s", 563 ddigits - 2, textsize, 564 ddigits - 2, datasize, 565 ddigits - 2, bsssize, 566 ddigits - 2, textsize + datasize + bsssize, 567 xdigits - 2, textsize + datasize + bsssize, 568 fname); 569 if (prefix != NULL) 570 printf (gettext (" (ex %s)"), prefix); 571 fputs_unlocked ("\n", stdout); 572 573 total_textsize += textsize; 574 total_datasize += datasize; 575 total_bsssize += bsssize; 576 577 totals_class = MAX (totals_class, gelf_getclass (elf)); 578} 579 580 581/* Show total size. */ 582static void 583show_bsd_totals (void) 584{ 585 int ddigits = length_map[totals_class - 1][radix_decimal]; 586 int xdigits = length_map[totals_class - 1][radix_hex]; 587 588 printf ("%*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" 589 PRIxMAX " %s", 590 ddigits - 2, total_textsize, 591 ddigits - 2, total_datasize, 592 ddigits - 2, total_bsssize, 593 ddigits - 2, total_textsize + total_datasize + total_bsssize, 594 xdigits - 2, total_textsize + total_datasize + total_bsssize, 595 gettext ("(TOTALS)\n")); 596} 597 598 599/* Show size and permission of loadable segments. */ 600static void 601show_segments (Elf *elf, const char *fullname) 602{ 603 size_t phnum; 604 if (elf_getphdrnum (elf, &phnum) != 0) 605 INTERNAL_ERROR (fullname); 606 607 GElf_Off total = 0; 608 bool first = true; 609 for (size_t cnt = 0; cnt < phnum; ++cnt) 610 { 611 GElf_Phdr phdr_mem; 612 GElf_Phdr *phdr; 613 614 phdr = gelf_getphdr (elf, cnt, &phdr_mem); 615 if (phdr == NULL) 616 INTERNAL_ERROR (fullname); 617 618 if (phdr->p_type != PT_LOAD) 619 /* Only load segments. */ 620 continue; 621 622 if (! first) 623 fputs_unlocked (" + ", stdout); 624 first = false; 625 626 printf (radix == radix_hex ? "%" PRIx64 "(%c%c%c)" 627 : (radix == radix_decimal ? "%" PRId64 "(%c%c%c)" 628 : "%" PRIo64 "(%c%c%c)"), 629 phdr->p_memsz, 630 (phdr->p_flags & PF_R) == 0 ? '-' : 'r', 631 (phdr->p_flags & PF_W) == 0 ? '-' : 'w', 632 (phdr->p_flags & PF_X) == 0 ? '-' : 'x'); 633 634 total += phdr->p_memsz; 635 } 636 637 if (radix == radix_hex) 638 printf (" = %#" PRIx64 "\n", total); 639 else if (radix == radix_decimal) 640 printf (" = %" PRId64 "\n", total); 641 else 642 printf (" = %" PRIo64 "\n", total); 643} 644 645 646static void 647handle_elf (Elf *elf, const char *prefix, const char *fname) 648{ 649 size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); 650 size_t fname_len = strlen (fname) + 1; 651 char fullname[prefix_len + 1 + fname_len]; 652 char *cp = fullname; 653 654 /* Create the full name of the file. */ 655 if (prefix != NULL) 656 { 657 cp = mempcpy (cp, prefix, prefix_len); 658 *cp++ = ':'; 659 } 660 memcpy (cp, fname, fname_len); 661 662 if (format == format_sysv) 663 show_sysv (elf, prefix, fname, fullname); 664 else if (format == format_sysv_one_line) 665 show_sysv_one_line (elf); 666 else if (format == format_segments) 667 show_segments (elf, fullname); 668 else 669 { 670 print_header (elf); 671 672 show_bsd (elf, prefix, fname, fullname); 673 } 674} 675 676 677#include "debugpred.h" 678