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