1 /* size.c -- report size of various sections of an executable file. 2 Copyright (C) 1991-2016 Free Software Foundation, Inc. 3 4 This file is part of GNU Binutils. 5 6 This program 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 This program is distributed in the hope that it will be useful, 12 but 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, write to the Free Software 18 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 19 MA 02110-1301, USA. */ 20 21 /* Extensions/incompatibilities: 22 o - BSD output has filenames at the end. 23 o - BSD output can appear in different radicies. 24 o - SysV output has less redundant whitespace. Filename comes at end. 25 o - SysV output doesn't show VMA which is always the same as the PMA. 26 o - We also handle core files. 27 o - We also handle archives. 28 If you write shell scripts which manipulate this info then you may be 29 out of luck; there's no --compatibility or --pedantic option. */ 30 31 #include "sysdep.h" 32 #include "bfd.h" 33 #include "libiberty.h" 34 #include "getopt.h" 35 #include "bucomm.h" 36 37 #ifndef BSD_DEFAULT 38 #define BSD_DEFAULT 1 39 #endif 40 41 /* Program options. */ 42 43 static enum 44 { 45 decimal, octal, hex 46 } 47 radix = decimal; 48 49 /* 0 means use AT&T-style output. */ 50 static int berkeley_format = BSD_DEFAULT; 51 52 static int show_version = 0; 53 static int show_help = 0; 54 static int show_totals = 0; 55 static int show_common = 0; 56 57 static bfd_size_type common_size; 58 static bfd_size_type total_bsssize; 59 static bfd_size_type total_datasize; 60 static bfd_size_type total_textsize; 61 62 /* Program exit status. */ 63 static int return_code = 0; 64 65 static char *target = NULL; 66 67 /* Forward declarations. */ 68 69 static void display_file (char *); 70 static void rprint_number (int, bfd_size_type); 71 static void print_sizes (bfd * file); 72 73 static void 74 usage (FILE *stream, int status) 75 { 76 fprintf (stream, _("Usage: %s [option(s)] [file(s)]\n"), program_name); 77 fprintf (stream, _(" Displays the sizes of sections inside binary files\n")); 78 fprintf (stream, _(" If no input file(s) are specified, a.out is assumed\n")); 79 fprintf (stream, _(" The options are:\n\ 80 -A|-B --format={sysv|berkeley} Select output style (default is %s)\n\ 81 -o|-d|-x --radix={8|10|16} Display numbers in octal, decimal or hex\n\ 82 -t --totals Display the total sizes (Berkeley only)\n\ 83 --common Display total size for *COM* syms\n\ 84 --target=<bfdname> Set the binary file format\n\ 85 @<file> Read options from <file>\n\ 86 -h --help Display this information\n\ 87 -v --version Display the program's version\n\ 88 \n"), 89 #if BSD_DEFAULT 90 "berkeley" 91 #else 92 "sysv" 93 #endif 94 ); 95 list_supported_targets (program_name, stream); 96 if (REPORT_BUGS_TO[0] && status == 0) 97 fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); 98 exit (status); 99 } 100 101 #define OPTION_FORMAT (200) 102 #define OPTION_RADIX (OPTION_FORMAT + 1) 103 #define OPTION_TARGET (OPTION_RADIX + 1) 104 105 static struct option long_options[] = 106 { 107 {"common", no_argument, &show_common, 1}, 108 {"format", required_argument, 0, OPTION_FORMAT}, 109 {"radix", required_argument, 0, OPTION_RADIX}, 110 {"target", required_argument, 0, OPTION_TARGET}, 111 {"totals", no_argument, &show_totals, 1}, 112 {"version", no_argument, &show_version, 1}, 113 {"help", no_argument, &show_help, 1}, 114 {0, no_argument, 0, 0} 115 }; 116 117 int main (int, char **); 118 119 int 120 main (int argc, char **argv) 121 { 122 int temp; 123 int c; 124 125 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) 126 setlocale (LC_MESSAGES, ""); 127 #endif 128 #if defined (HAVE_SETLOCALE) 129 setlocale (LC_CTYPE, ""); 130 #endif 131 bindtextdomain (PACKAGE, LOCALEDIR); 132 textdomain (PACKAGE); 133 134 program_name = *argv; 135 xmalloc_set_program_name (program_name); 136 bfd_set_error_program_name (program_name); 137 138 expandargv (&argc, &argv); 139 140 bfd_init (); 141 set_default_bfd_target (); 142 143 while ((c = getopt_long (argc, argv, "ABHhVvdfotx", long_options, 144 (int *) 0)) != EOF) 145 switch (c) 146 { 147 case OPTION_FORMAT: 148 switch (*optarg) 149 { 150 case 'B': 151 case 'b': 152 berkeley_format = 1; 153 break; 154 case 'S': 155 case 's': 156 berkeley_format = 0; 157 break; 158 default: 159 non_fatal (_("invalid argument to --format: %s"), optarg); 160 usage (stderr, 1); 161 } 162 break; 163 164 case OPTION_TARGET: 165 target = optarg; 166 break; 167 168 case OPTION_RADIX: 169 #ifdef ANSI_LIBRARIES 170 temp = strtol (optarg, NULL, 10); 171 #else 172 temp = atol (optarg); 173 #endif 174 switch (temp) 175 { 176 case 10: 177 radix = decimal; 178 break; 179 case 8: 180 radix = octal; 181 break; 182 case 16: 183 radix = hex; 184 break; 185 default: 186 non_fatal (_("Invalid radix: %s\n"), optarg); 187 usage (stderr, 1); 188 } 189 break; 190 191 case 'A': 192 berkeley_format = 0; 193 break; 194 case 'B': 195 berkeley_format = 1; 196 break; 197 case 'v': 198 case 'V': 199 show_version = 1; 200 break; 201 case 'd': 202 radix = decimal; 203 break; 204 case 'x': 205 radix = hex; 206 break; 207 case 'o': 208 radix = octal; 209 break; 210 case 't': 211 show_totals = 1; 212 break; 213 case 'f': /* FIXME : For sysv68, `-f' means `full format', i.e. 214 `[fname:] M(.text) + N(.data) + O(.bss) + P(.comment) = Q' 215 where `fname: ' appears only if there are >= 2 input files, 216 and M, N, O, P, Q are expressed in decimal by default, 217 hexa or octal if requested by `-x' or `-o'. 218 Just to make things interesting, Solaris also accepts -f, 219 which prints out the size of each allocatable section, the 220 name of the section, and the total of the section sizes. */ 221 /* For the moment, accept `-f' silently, and ignore it. */ 222 break; 223 case 0: 224 break; 225 case 'h': 226 case 'H': 227 case '?': 228 usage (stderr, 1); 229 } 230 231 if (show_version) 232 print_version ("size"); 233 if (show_help) 234 usage (stdout, 0); 235 236 if (optind == argc) 237 display_file ("a.out"); 238 else 239 for (; optind < argc;) 240 display_file (argv[optind++]); 241 242 if (show_totals && berkeley_format) 243 { 244 bfd_size_type total = total_textsize + total_datasize + total_bsssize; 245 246 rprint_number (7, total_textsize); 247 putchar('\t'); 248 rprint_number (7, total_datasize); 249 putchar('\t'); 250 rprint_number (7, total_bsssize); 251 printf (((radix == octal) ? "\t%7lo\t%7lx\t" : "\t%7lu\t%7lx\t"), 252 (unsigned long) total, (unsigned long) total); 253 fputs ("(TOTALS)\n", stdout); 254 } 255 256 return return_code; 257 } 258 259 /* Total size required for common symbols in ABFD. */ 260 261 static void 262 calculate_common_size (bfd *abfd) 263 { 264 asymbol **syms = NULL; 265 long storage, symcount; 266 267 common_size = 0; 268 if ((bfd_get_file_flags (abfd) & (EXEC_P | DYNAMIC | HAS_SYMS)) != HAS_SYMS) 269 return; 270 271 storage = bfd_get_symtab_upper_bound (abfd); 272 if (storage < 0) 273 bfd_fatal (bfd_get_filename (abfd)); 274 if (storage) 275 syms = (asymbol **) xmalloc (storage); 276 277 symcount = bfd_canonicalize_symtab (abfd, syms); 278 if (symcount < 0) 279 bfd_fatal (bfd_get_filename (abfd)); 280 281 while (--symcount >= 0) 282 { 283 asymbol *sym = syms[symcount]; 284 285 if (bfd_is_com_section (sym->section) 286 && (sym->flags & BSF_SECTION_SYM) == 0) 287 common_size += sym->value; 288 } 289 free (syms); 290 } 291 292 /* Display stats on file or archive member ABFD. */ 293 294 static void 295 display_bfd (bfd *abfd) 296 { 297 char **matching; 298 299 if (bfd_check_format (abfd, bfd_archive)) 300 /* An archive within an archive. */ 301 return; 302 303 if (bfd_check_format_matches (abfd, bfd_object, &matching)) 304 { 305 print_sizes (abfd); 306 printf ("\n"); 307 return; 308 } 309 310 if (bfd_get_error () == bfd_error_file_ambiguously_recognized) 311 { 312 bfd_nonfatal (bfd_get_filename (abfd)); 313 list_matching_formats (matching); 314 free (matching); 315 return_code = 3; 316 return; 317 } 318 319 if (bfd_check_format_matches (abfd, bfd_core, &matching)) 320 { 321 const char *core_cmd; 322 323 print_sizes (abfd); 324 fputs (" (core file", stdout); 325 326 core_cmd = bfd_core_file_failing_command (abfd); 327 if (core_cmd) 328 printf (" invoked as %s", core_cmd); 329 330 puts (")\n"); 331 return; 332 } 333 334 bfd_nonfatal (bfd_get_filename (abfd)); 335 336 if (bfd_get_error () == bfd_error_file_ambiguously_recognized) 337 { 338 list_matching_formats (matching); 339 free (matching); 340 } 341 342 return_code = 3; 343 } 344 345 static void 346 display_archive (bfd *file) 347 { 348 bfd *arfile = (bfd *) NULL; 349 bfd *last_arfile = (bfd *) NULL; 350 351 for (;;) 352 { 353 bfd_set_error (bfd_error_no_error); 354 355 arfile = bfd_openr_next_archived_file (file, arfile); 356 if (arfile == NULL) 357 { 358 if (bfd_get_error () != bfd_error_no_more_archived_files) 359 { 360 bfd_nonfatal (bfd_get_filename (file)); 361 return_code = 2; 362 } 363 break; 364 } 365 366 display_bfd (arfile); 367 368 if (last_arfile != NULL) 369 { 370 bfd_close (last_arfile); 371 372 /* PR 17512: file: a244edbc. */ 373 if (last_arfile == arfile) 374 return; 375 } 376 377 last_arfile = arfile; 378 } 379 380 if (last_arfile != NULL) 381 bfd_close (last_arfile); 382 } 383 384 static void 385 display_file (char *filename) 386 { 387 bfd *file; 388 389 if (get_file_size (filename) < 1) 390 { 391 return_code = 1; 392 return; 393 } 394 395 file = bfd_openr (filename, target); 396 if (file == NULL) 397 { 398 bfd_nonfatal (filename); 399 return_code = 1; 400 return; 401 } 402 403 if (bfd_check_format (file, bfd_archive)) 404 display_archive (file); 405 else 406 display_bfd (file); 407 408 if (!bfd_close (file)) 409 { 410 bfd_nonfatal (filename); 411 return_code = 1; 412 return; 413 } 414 } 415 416 static int 417 size_number (bfd_size_type num) 418 { 419 char buffer[40]; 420 421 sprintf (buffer, 422 (radix == decimal ? "%" BFD_VMA_FMT "u" : 423 ((radix == octal) ? "0%" BFD_VMA_FMT "o" : "0x%" BFD_VMA_FMT "x")), 424 num); 425 426 return strlen (buffer); 427 } 428 429 static void 430 rprint_number (int width, bfd_size_type num) 431 { 432 char buffer[40]; 433 434 sprintf (buffer, 435 (radix == decimal ? "%" BFD_VMA_FMT "u" : 436 ((radix == octal) ? "0%" BFD_VMA_FMT "o" : "0x%" BFD_VMA_FMT "x")), 437 num); 438 439 printf ("%*s", width, buffer); 440 } 441 442 static bfd_size_type bsssize; 443 static bfd_size_type datasize; 444 static bfd_size_type textsize; 445 446 static void 447 berkeley_sum (bfd *abfd ATTRIBUTE_UNUSED, sec_ptr sec, 448 void *ignore ATTRIBUTE_UNUSED) 449 { 450 flagword flags; 451 bfd_size_type size; 452 453 flags = bfd_get_section_flags (abfd, sec); 454 if ((flags & SEC_ALLOC) == 0) 455 return; 456 457 size = bfd_get_section_size (sec); 458 if ((flags & SEC_CODE) != 0 || (flags & SEC_READONLY) != 0) 459 textsize += size; 460 else if ((flags & SEC_HAS_CONTENTS) != 0) 461 datasize += size; 462 else 463 bsssize += size; 464 } 465 466 static void 467 print_berkeley_format (bfd *abfd) 468 { 469 static int files_seen = 0; 470 bfd_size_type total; 471 472 bsssize = 0; 473 datasize = 0; 474 textsize = 0; 475 476 bfd_map_over_sections (abfd, berkeley_sum, NULL); 477 478 bsssize += common_size; 479 if (files_seen++ == 0) 480 puts ((radix == octal) ? " text\t data\t bss\t oct\t hex\tfilename" : 481 " text\t data\t bss\t dec\t hex\tfilename"); 482 483 total = textsize + datasize + bsssize; 484 485 if (show_totals) 486 { 487 total_textsize += textsize; 488 total_datasize += datasize; 489 total_bsssize += bsssize; 490 } 491 492 rprint_number (7, textsize); 493 putchar ('\t'); 494 rprint_number (7, datasize); 495 putchar ('\t'); 496 rprint_number (7, bsssize); 497 printf (((radix == octal) ? "\t%7lo\t%7lx\t" : "\t%7lu\t%7lx\t"), 498 (unsigned long) total, (unsigned long) total); 499 500 fputs (bfd_get_filename (abfd), stdout); 501 502 if (abfd->my_archive) 503 printf (" (ex %s)", bfd_get_filename (abfd->my_archive)); 504 } 505 506 /* I REALLY miss lexical functions! */ 507 bfd_size_type svi_total = 0; 508 bfd_vma svi_maxvma = 0; 509 int svi_namelen = 0; 510 int svi_vmalen = 0; 511 int svi_sizelen = 0; 512 513 static void 514 sysv_internal_sizer (bfd *file ATTRIBUTE_UNUSED, sec_ptr sec, 515 void *ignore ATTRIBUTE_UNUSED) 516 { 517 bfd_size_type size = bfd_section_size (file, sec); 518 519 if ( ! bfd_is_abs_section (sec) 520 && ! bfd_is_com_section (sec) 521 && ! bfd_is_und_section (sec)) 522 { 523 int namelen = strlen (bfd_section_name (file, sec)); 524 525 if (namelen > svi_namelen) 526 svi_namelen = namelen; 527 528 svi_total += size; 529 530 if (bfd_section_vma (file, sec) > svi_maxvma) 531 svi_maxvma = bfd_section_vma (file, sec); 532 } 533 } 534 535 static void 536 sysv_one_line (const char *name, bfd_size_type size, bfd_vma vma) 537 { 538 printf ("%-*s ", svi_namelen, name); 539 rprint_number (svi_sizelen, size); 540 printf (" "); 541 rprint_number (svi_vmalen, vma); 542 printf ("\n"); 543 } 544 545 static void 546 sysv_internal_printer (bfd *file ATTRIBUTE_UNUSED, sec_ptr sec, 547 void *ignore ATTRIBUTE_UNUSED) 548 { 549 bfd_size_type size = bfd_section_size (file, sec); 550 551 if ( ! bfd_is_abs_section (sec) 552 && ! bfd_is_com_section (sec) 553 && ! bfd_is_und_section (sec)) 554 { 555 svi_total += size; 556 557 sysv_one_line (bfd_section_name (file, sec), 558 size, 559 bfd_section_vma (file, sec)); 560 } 561 } 562 563 static void 564 print_sysv_format (bfd *file) 565 { 566 /* Size all of the columns. */ 567 svi_total = 0; 568 svi_maxvma = 0; 569 svi_namelen = 0; 570 bfd_map_over_sections (file, sysv_internal_sizer, NULL); 571 if (show_common) 572 { 573 if (svi_namelen < (int) sizeof ("*COM*") - 1) 574 svi_namelen = sizeof ("*COM*") - 1; 575 svi_total += common_size; 576 } 577 578 svi_vmalen = size_number ((bfd_size_type)svi_maxvma); 579 580 if ((size_t) svi_vmalen < sizeof ("addr") - 1) 581 svi_vmalen = sizeof ("addr")-1; 582 583 svi_sizelen = size_number (svi_total); 584 if ((size_t) svi_sizelen < sizeof ("size") - 1) 585 svi_sizelen = sizeof ("size")-1; 586 587 svi_total = 0; 588 printf ("%s ", bfd_get_filename (file)); 589 590 if (file->my_archive) 591 printf (" (ex %s)", bfd_get_filename (file->my_archive)); 592 593 printf (":\n%-*s %*s %*s\n", svi_namelen, "section", 594 svi_sizelen, "size", svi_vmalen, "addr"); 595 596 bfd_map_over_sections (file, sysv_internal_printer, NULL); 597 if (show_common) 598 { 599 svi_total += common_size; 600 sysv_one_line ("*COM*", common_size, 0); 601 } 602 603 printf ("%-*s ", svi_namelen, "Total"); 604 rprint_number (svi_sizelen, svi_total); 605 printf ("\n\n"); 606 } 607 608 static void 609 print_sizes (bfd *file) 610 { 611 if (show_common) 612 calculate_common_size (file); 613 if (berkeley_format) 614 print_berkeley_format (file); 615 else 616 print_sysv_format (file); 617 } 618