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. */ 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 main(int argc,char * argv[])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 print_version(FILE * stream,struct argp_state * state)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 parse_opt(int key,char * arg,struct argp_state * state)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 process_file(const char * fname)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 print_header(Elf * elf)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 handle_ar(int fd,Elf * elf,const char * prefix,const char * fname)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 show_sysv(Elf * elf,const char * prefix,const char * fname,const char * fullname)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 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. */ 479 static void show_sysv_one_line(Elf * elf)480 show_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. */ 523 static uintmax_t total_textsize; 524 static uintmax_t total_datasize; 525 static uintmax_t total_bsssize; 526 527 528 /* Show sizes in BSD format. */ 529 static void show_bsd(Elf * elf,const char * prefix,const char * fname,const char * fullname)530 show_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. */ 582 static void show_bsd_totals(void)583 show_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. */ 600 static void show_segments(Elf * elf,const char * fullname)601 show_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 646 static void handle_elf(Elf * elf,const char * prefix,const char * fname)647 handle_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