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