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 Free Software Foundation, Inc. 4 5 This file is part of GNU Binutils. 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, 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 "bfd.h" 32 #include "bucomm.h" 33 #include "libiberty.h" 34 #include "getopt.h" 35 36 #ifndef BSD_DEFAULT 37 #define BSD_DEFAULT 1 38 #endif 39 40 /* Program options. */ 41 42 enum 43 { 44 decimal, octal, hex 45 } 46 radix = decimal; 47 48 /* 0 means use AT&T-style output. */ 49 static int berkeley_format = BSD_DEFAULT; 50 51 int show_version = 0; 52 int show_help = 0; 53 int show_totals = 0; 54 55 static bfd_size_type total_bsssize; 56 static bfd_size_type total_datasize; 57 static bfd_size_type total_textsize; 58 59 /* Program exit status. */ 60 int return_code = 0; 61 62 static char *target = NULL; 63 64 /* Static declarations. */ 65 66 static void usage (FILE *, int); 67 static void display_file (char *); 68 static void display_bfd (bfd *); 69 static void display_archive (bfd *); 70 static int size_number (bfd_size_type); 71 static void rprint_number (int, bfd_size_type); 72 static void print_berkeley_format (bfd *); 73 static void sysv_internal_sizer (bfd *, asection *, void *); 74 static void sysv_internal_printer (bfd *, asection *, void *); 75 static void print_sysv_format (bfd *); 76 static void print_sizes (bfd * file); 77 static void berkeley_sum (bfd *, sec_ptr, void *); 78 79 static void 80 usage (FILE *stream, int status) 81 { 82 fprintf (stream, _("Usage: %s [option(s)] [file(s)]\n"), program_name); 83 fprintf (stream, _(" Displays the sizes of sections inside binary files\n")); 84 fprintf (stream, _(" If no input file(s) are specified, a.out is assumed\n")); 85 fprintf (stream, _(" The options are:\n\ 86 -A|-B --format={sysv|berkeley} Select output style (default is %s)\n\ 87 -o|-d|-x --radix={8|10|16} Display numbers in octal, decimal or hex\n\ 88 -t --totals Display the total sizes (Berkeley only)\n\ 89 --target=<bfdname> Set the binary file format\n\ 90 @<file> Read options from <file>\n\ 91 -h --help Display this information\n\ 92 -v --version Display the program's version\n\ 93 \n"), 94 #if BSD_DEFAULT 95 "berkeley" 96 #else 97 "sysv" 98 #endif 99 ); 100 list_supported_targets (program_name, stream); 101 if (status == 0) 102 fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); 103 exit (status); 104 } 105 106 static struct option long_options[] = 107 { 108 {"format", required_argument, 0, 200}, 109 {"radix", required_argument, 0, 201}, 110 {"target", required_argument, 0, 202}, 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 137 expandargv (&argc, &argv); 138 139 bfd_init (); 140 set_default_bfd_target (); 141 142 while ((c = getopt_long (argc, argv, "ABHhVvdfotx", long_options, 143 (int *) 0)) != EOF) 144 switch (c) 145 { 146 case 200: /* --format */ 147 switch (*optarg) 148 { 149 case 'B': 150 case 'b': 151 berkeley_format = 1; 152 break; 153 case 'S': 154 case 's': 155 berkeley_format = 0; 156 break; 157 default: 158 non_fatal (_("invalid argument to --format: %s"), optarg); 159 usage (stderr, 1); 160 } 161 break; 162 163 case 202: /* --target */ 164 target = optarg; 165 break; 166 167 case 201: /* --radix */ 168 #ifdef ANSI_LIBRARIES 169 temp = strtol (optarg, NULL, 10); 170 #else 171 temp = atol (optarg); 172 #endif 173 switch (temp) 174 { 175 case 10: 176 radix = decimal; 177 break; 178 case 8: 179 radix = octal; 180 break; 181 case 16: 182 radix = hex; 183 break; 184 default: 185 non_fatal (_("Invalid radix: %s\n"), optarg); 186 usage (stderr, 1); 187 } 188 break; 189 190 case 'A': 191 berkeley_format = 0; 192 break; 193 case 'B': 194 berkeley_format = 1; 195 break; 196 case 'v': 197 case 'V': 198 show_version = 1; 199 break; 200 case 'd': 201 radix = decimal; 202 break; 203 case 'x': 204 radix = hex; 205 break; 206 case 'o': 207 radix = octal; 208 break; 209 case 't': 210 show_totals = 1; 211 break; 212 case 'f': /* FIXME : For sysv68, `-f' means `full format', i.e. 213 `[fname:] M(.text) + N(.data) + O(.bss) + P(.comment) = Q' 214 where `fname: ' appears only if there are >= 2 input files, 215 and M, N, O, P, Q are expressed in decimal by default, 216 hexa or octal if requested by `-x' or `-o'. 217 Just to make things interesting, Solaris also accepts -f, 218 which prints out the size of each allocatable section, the 219 name of the section, and the total of the section sizes. */ 220 /* For the moment, accept `-f' silently, and ignore it. */ 221 break; 222 case 0: 223 break; 224 case 'h': 225 case 'H': 226 case '?': 227 usage (stderr, 1); 228 } 229 230 if (show_version) 231 print_version ("size"); 232 if (show_help) 233 usage (stdout, 0); 234 235 if (optind == argc) 236 display_file ("a.out"); 237 else 238 for (; optind < argc;) 239 display_file (argv[optind++]); 240 241 if (show_totals && berkeley_format) 242 { 243 bfd_size_type total = total_textsize + total_datasize + total_bsssize; 244 245 rprint_number (7, total_textsize); 246 putchar('\t'); 247 rprint_number (7, total_datasize); 248 putchar('\t'); 249 rprint_number (7, total_bsssize); 250 printf (((radix == octal) ? "\t%7lo\t%7lx\t" : "\t%7lu\t%7lx\t"), 251 (unsigned long) total, (unsigned long) total); 252 fputs ("(TOTALS)\n", stdout); 253 } 254 255 return return_code; 256 } 257 258 /* Display stats on file or archive member ABFD. */ 259 260 static void 261 display_bfd (bfd *abfd) 262 { 263 char **matching; 264 265 if (bfd_check_format (abfd, bfd_archive)) 266 /* An archive within an archive. */ 267 return; 268 269 if (bfd_check_format_matches (abfd, bfd_object, &matching)) 270 { 271 print_sizes (abfd); 272 printf ("\n"); 273 return; 274 } 275 276 if (bfd_get_error () == bfd_error_file_ambiguously_recognized) 277 { 278 bfd_nonfatal (bfd_get_filename (abfd)); 279 list_matching_formats (matching); 280 free (matching); 281 return_code = 3; 282 return; 283 } 284 285 if (bfd_check_format_matches (abfd, bfd_core, &matching)) 286 { 287 const char *core_cmd; 288 289 print_sizes (abfd); 290 fputs (" (core file", stdout); 291 292 core_cmd = bfd_core_file_failing_command (abfd); 293 if (core_cmd) 294 printf (" invoked as %s", core_cmd); 295 296 puts (")\n"); 297 return; 298 } 299 300 bfd_nonfatal (bfd_get_filename (abfd)); 301 302 if (bfd_get_error () == bfd_error_file_ambiguously_recognized) 303 { 304 list_matching_formats (matching); 305 free (matching); 306 } 307 308 return_code = 3; 309 } 310 311 static void 312 display_archive (bfd *file) 313 { 314 bfd *arfile = (bfd *) NULL; 315 bfd *last_arfile = (bfd *) NULL; 316 317 for (;;) 318 { 319 bfd_set_error (bfd_error_no_error); 320 321 arfile = bfd_openr_next_archived_file (file, arfile); 322 if (arfile == NULL) 323 { 324 if (bfd_get_error () != bfd_error_no_more_archived_files) 325 { 326 bfd_nonfatal (bfd_get_filename (file)); 327 return_code = 2; 328 } 329 break; 330 } 331 332 display_bfd (arfile); 333 334 if (last_arfile != NULL) 335 bfd_close (last_arfile); 336 last_arfile = arfile; 337 } 338 339 if (last_arfile != NULL) 340 bfd_close (last_arfile); 341 } 342 343 static void 344 display_file (char *filename) 345 { 346 bfd *file; 347 348 if (get_file_size (filename) < 1) 349 return; 350 351 file = bfd_openr (filename, target); 352 if (file == NULL) 353 { 354 bfd_nonfatal (filename); 355 return_code = 1; 356 return; 357 } 358 359 if (bfd_check_format (file, bfd_archive)) 360 display_archive (file); 361 else 362 display_bfd (file); 363 364 if (!bfd_close (file)) 365 { 366 bfd_nonfatal (filename); 367 return_code = 1; 368 return; 369 } 370 } 371 372 /* This is what lexical functions are for. */ 373 374 static int 375 size_number (bfd_size_type num) 376 { 377 char buffer[40]; 378 379 sprintf (buffer, 380 (radix == decimal ? "%lu" : 381 ((radix == octal) ? "0%lo" : "0x%lx")), 382 (unsigned long) num); 383 384 return strlen (buffer); 385 } 386 387 static void 388 rprint_number (int width, bfd_size_type num) 389 { 390 char buffer[40]; 391 392 sprintf (buffer, 393 (radix == decimal ? "%lu" : 394 ((radix == octal) ? "0%lo" : "0x%lx")), 395 (unsigned long) num); 396 397 printf ("%*s", width, buffer); 398 } 399 400 static bfd_size_type bsssize; 401 static bfd_size_type datasize; 402 static bfd_size_type textsize; 403 404 static void 405 berkeley_sum (bfd *abfd ATTRIBUTE_UNUSED, sec_ptr sec, 406 void *ignore ATTRIBUTE_UNUSED) 407 { 408 flagword flags; 409 bfd_size_type size; 410 411 flags = bfd_get_section_flags (abfd, sec); 412 if ((flags & SEC_ALLOC) == 0) 413 return; 414 415 size = bfd_get_section_size (sec); 416 if ((flags & SEC_CODE) != 0 || (flags & SEC_READONLY) != 0) 417 textsize += size; 418 else if ((flags & SEC_HAS_CONTENTS) != 0) 419 datasize += size; 420 else 421 bsssize += size; 422 } 423 424 static void 425 print_berkeley_format (bfd *abfd) 426 { 427 static int files_seen = 0; 428 bfd_size_type total; 429 430 bsssize = 0; 431 datasize = 0; 432 textsize = 0; 433 434 bfd_map_over_sections (abfd, berkeley_sum, NULL); 435 436 if (files_seen++ == 0) 437 puts ((radix == octal) ? " text\t data\t bss\t oct\t hex\tfilename" : 438 " text\t data\t bss\t dec\t hex\tfilename"); 439 440 total = textsize + datasize + bsssize; 441 442 if (show_totals) 443 { 444 total_textsize += textsize; 445 total_datasize += datasize; 446 total_bsssize += bsssize; 447 } 448 449 rprint_number (7, textsize); 450 putchar ('\t'); 451 rprint_number (7, datasize); 452 putchar ('\t'); 453 rprint_number (7, bsssize); 454 printf (((radix == octal) ? "\t%7lo\t%7lx\t" : "\t%7lu\t%7lx\t"), 455 (unsigned long) total, (unsigned long) total); 456 457 fputs (bfd_get_filename (abfd), stdout); 458 459 if (bfd_my_archive (abfd)) 460 printf (" (ex %s)", bfd_get_filename (bfd_my_archive (abfd))); 461 } 462 463 /* I REALLY miss lexical functions! */ 464 bfd_size_type svi_total = 0; 465 bfd_vma svi_maxvma = 0; 466 int svi_namelen = 0; 467 int svi_vmalen = 0; 468 int svi_sizelen = 0; 469 470 static void 471 sysv_internal_sizer (bfd *file ATTRIBUTE_UNUSED, sec_ptr sec, 472 void *ignore ATTRIBUTE_UNUSED) 473 { 474 bfd_size_type size = bfd_section_size (file, sec); 475 476 if ( ! bfd_is_abs_section (sec) 477 && ! bfd_is_com_section (sec) 478 && ! bfd_is_und_section (sec)) 479 { 480 int namelen = strlen (bfd_section_name (file, sec)); 481 482 if (namelen > svi_namelen) 483 svi_namelen = namelen; 484 485 svi_total += size; 486 487 if (bfd_section_vma (file, sec) > svi_maxvma) 488 svi_maxvma = bfd_section_vma (file, sec); 489 } 490 } 491 492 static void 493 sysv_internal_printer (bfd *file ATTRIBUTE_UNUSED, sec_ptr sec, 494 void *ignore ATTRIBUTE_UNUSED) 495 { 496 bfd_size_type size = bfd_section_size (file, sec); 497 498 if ( ! bfd_is_abs_section (sec) 499 && ! bfd_is_com_section (sec) 500 && ! bfd_is_und_section (sec)) 501 { 502 svi_total += size; 503 504 printf ("%-*s ", svi_namelen, bfd_section_name (file, sec)); 505 rprint_number (svi_sizelen, size); 506 printf (" "); 507 rprint_number (svi_vmalen, bfd_section_vma (file, sec)); 508 printf ("\n"); 509 } 510 } 511 512 static void 513 print_sysv_format (bfd *file) 514 { 515 /* Size all of the columns. */ 516 svi_total = 0; 517 svi_maxvma = 0; 518 svi_namelen = 0; 519 bfd_map_over_sections (file, sysv_internal_sizer, NULL); 520 svi_vmalen = size_number ((bfd_size_type)svi_maxvma); 521 522 if ((size_t) svi_vmalen < sizeof ("addr") - 1) 523 svi_vmalen = sizeof ("addr")-1; 524 525 svi_sizelen = size_number (svi_total); 526 if ((size_t) svi_sizelen < sizeof ("size") - 1) 527 svi_sizelen = sizeof ("size")-1; 528 529 svi_total = 0; 530 printf ("%s ", bfd_get_filename (file)); 531 532 if (bfd_my_archive (file)) 533 printf (" (ex %s)", bfd_get_filename (bfd_my_archive (file))); 534 535 printf (":\n%-*s %*s %*s\n", svi_namelen, "section", 536 svi_sizelen, "size", svi_vmalen, "addr"); 537 538 bfd_map_over_sections (file, sysv_internal_printer, NULL); 539 540 printf ("%-*s ", svi_namelen, "Total"); 541 rprint_number (svi_sizelen, svi_total); 542 printf ("\n\n"); 543 } 544 545 static void 546 print_sizes (bfd *file) 547 { 548 if (berkeley_format) 549 print_berkeley_format (file); 550 else 551 print_sysv_format (file); 552 } 553