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