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