size.c revision 91041
1/* size.c -- report size of various sections of an executable file. 2 Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 3 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 42enum 43 { 44 decimal, octal, hex 45 } 46radix = decimal; 47 48int berkeley_format = BSD_DEFAULT; /* 0 means use AT&T-style output. */ 49int show_version = 0; 50int show_help = 0; 51int show_totals = 0; 52 53static bfd_size_type total_bsssize; 54static bfd_size_type total_datasize; 55static bfd_size_type total_textsize; 56 57/* Program exit status. */ 58int return_code = 0; 59 60static char *target = NULL; 61 62/* Static declarations. */ 63 64static void usage PARAMS ((FILE *, int)); 65static void display_file PARAMS ((char *)); 66static void display_bfd PARAMS ((bfd *)); 67static void display_archive PARAMS ((bfd *)); 68static int size_number PARAMS ((bfd_size_type)); 69#if 0 70static void lprint_number PARAMS ((int, bfd_size_type)); 71#endif 72static void rprint_number PARAMS ((int, bfd_size_type)); 73static void print_berkeley_format PARAMS ((bfd *)); 74static void sysv_internal_sizer PARAMS ((bfd *, asection *, PTR)); 75static void sysv_internal_printer PARAMS ((bfd *, asection *, PTR)); 76static void print_sysv_format PARAMS ((bfd *)); 77static void print_sizes PARAMS ((bfd * file)); 78static void berkeley_sum PARAMS ((bfd *, sec_ptr, PTR)); 79 80static void 81usage (stream, status) 82 FILE *stream; 83 int status; 84{ 85 fprintf (stream, _("Usage: %s [option(s)] [file(s)]\n"), program_name); 86 fprintf (stream, _(" Displays the sizes of sections inside binary files\n")); 87 fprintf (stream, _(" If no input file(s) are specified, a.out is assumed\n")); 88 fprintf (stream, _(" The options are:\n\ 89 -A|-B --format={sysv|berkeley} Select output style (default is %s)\n\ 90 -o|-d|-h --radix={8|10|16} Display numbers in octal, decimal or hex\n\ 91 -t --totals Display the total sizes (Berkeley only)\n\ 92 --target=<bfdname> Set the binary file format\n\ 93 -h --help Display this information\n\ 94 -v --version Display the program's version\n\ 95\n"), 96#if BSD_DEFAULT 97 "berkeley" 98#else 99 "sysv" 100#endif 101); 102 list_supported_targets (program_name, stream); 103 if (status == 0) 104 fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); 105 exit (status); 106} 107 108struct option long_options[] = 109{ 110 {"format", required_argument, 0, 200}, 111 {"radix", required_argument, 0, 201}, 112 {"target", required_argument, 0, 202}, 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 119int main PARAMS ((int, char **)); 120 121int 122main (argc, argv) 123 int argc; 124 char **argv; 125{ 126 int temp; 127 int c; 128 129#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) 130 setlocale (LC_MESSAGES, ""); 131#endif 132#if defined (HAVE_SETLOCALE) 133 setlocale (LC_CTYPE, ""); 134#endif 135 bindtextdomain (PACKAGE, LOCALEDIR); 136 textdomain (PACKAGE); 137 138 program_name = *argv; 139 xmalloc_set_program_name (program_name); 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 200: /* --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 202: /* --target */ 166 target = optarg; 167 break; 168 169 case 201: /* --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/* Display stats on file or archive member ABFD. */ 261 262static void 263display_bfd (abfd) 264 bfd *abfd; 265{ 266 char **matching; 267 268 if (bfd_check_format (abfd, bfd_archive)) 269 /* An archive within an archive. */ 270 return; 271 272 if (bfd_check_format_matches (abfd, bfd_object, &matching)) 273 { 274 print_sizes (abfd); 275 printf ("\n"); 276 return; 277 } 278 279 if (bfd_get_error () == bfd_error_file_ambiguously_recognized) 280 { 281 bfd_nonfatal (bfd_get_filename (abfd)); 282 list_matching_formats (matching); 283 free (matching); 284 return_code = 3; 285 return; 286 } 287 288 if (bfd_check_format_matches (abfd, bfd_core, &matching)) 289 { 290 const char *core_cmd; 291 292 print_sizes (abfd); 293 fputs (" (core file", stdout); 294 295 core_cmd = bfd_core_file_failing_command (abfd); 296 if (core_cmd) 297 printf (" invoked as %s", core_cmd); 298 299 puts (")\n"); 300 return; 301 } 302 303 bfd_nonfatal (bfd_get_filename (abfd)); 304 305 if (bfd_get_error () == bfd_error_file_ambiguously_recognized) 306 { 307 list_matching_formats (matching); 308 free (matching); 309 } 310 311 return_code = 3; 312} 313 314static void 315display_archive (file) 316 bfd *file; 317{ 318 bfd *arfile = (bfd *) NULL; 319 320 for (;;) 321 { 322 bfd_set_error (bfd_error_no_error); 323 324 arfile = bfd_openr_next_archived_file (file, arfile); 325 if (arfile == NULL) 326 { 327 if (bfd_get_error () != bfd_error_no_more_archived_files) 328 { 329 bfd_nonfatal (bfd_get_filename (file)); 330 return_code = 2; 331 } 332 break; 333 } 334 335 display_bfd (arfile); 336 /* Don't close the archive elements; we need them for next_archive. */ 337 } 338} 339 340static void 341display_file (filename) 342 char *filename; 343{ 344 bfd *file = bfd_openr (filename, target); 345 346 if (file == NULL) 347 { 348 bfd_nonfatal (filename); 349 return_code = 1; 350 return; 351 } 352 353 if (bfd_check_format (file, bfd_archive) == true) 354 display_archive (file); 355 else 356 display_bfd (file); 357 358 if (bfd_close (file) == false) 359 { 360 bfd_nonfatal (filename); 361 return_code = 1; 362 return; 363 } 364} 365 366/* This is what lexical functions are for. */ 367 368static int 369size_number (num) 370 bfd_size_type num; 371{ 372 char buffer[40]; 373 374 sprintf (buffer, 375 (radix == decimal ? "%lu" : 376 ((radix == octal) ? "0%lo" : "0x%lx")), 377 (unsigned long) num); 378 379 return strlen (buffer); 380} 381 382#if 0 383 384/* This is not used. */ 385 386static void 387lprint_number (width, num) 388 int width; 389 bfd_size_type num; 390{ 391 char buffer[40]; 392 393 sprintf (buffer, 394 (radix == decimal ? "%lu" : 395 ((radix == octal) ? "0%lo" : "0x%lx")), 396 (unsigned long) num); 397 398 printf ("%-*s", width, buffer); 399} 400 401#endif 402 403static void 404rprint_number (width, num) 405 int width; 406 bfd_size_type num; 407{ 408 char buffer[40]; 409 410 sprintf (buffer, 411 (radix == decimal ? "%lu" : 412 ((radix == octal) ? "0%lo" : "0x%lx")), 413 (unsigned long) num); 414 415 printf ("%*s", width, buffer); 416} 417 418static bfd_size_type bsssize; 419static bfd_size_type datasize; 420static bfd_size_type textsize; 421 422static void 423berkeley_sum (abfd, sec, ignore) 424 bfd *abfd ATTRIBUTE_UNUSED; 425 sec_ptr sec; 426 PTR ignore ATTRIBUTE_UNUSED; 427{ 428 flagword flags; 429 bfd_size_type size; 430 431 flags = bfd_get_section_flags (abfd, sec); 432 if ((flags & SEC_ALLOC) == 0) 433 return; 434 435 size = bfd_get_section_size_before_reloc (sec); 436 if ((flags & SEC_CODE) != 0 || (flags & SEC_READONLY) != 0) 437 textsize += size; 438 else if ((flags & SEC_HAS_CONTENTS) != 0) 439 datasize += size; 440 else 441 bsssize += size; 442} 443 444static void 445print_berkeley_format (abfd) 446 bfd *abfd; 447{ 448 static int files_seen = 0; 449 bfd_size_type total; 450 451 bsssize = 0; 452 datasize = 0; 453 textsize = 0; 454 455 bfd_map_over_sections (abfd, berkeley_sum, (PTR) NULL); 456 457 if (files_seen++ == 0) 458#if 0 459 /* Intel doesn't like bss/stk because they don't have core files. */ 460 puts ((radix == octal) ? " text\t data\tbss/stk\t oct\t hex\tfilename" : 461 " text\t data\tbss/stk\t dec\t hex\tfilename"); 462#else 463 puts ((radix == octal) ? " text\t data\t bss\t oct\t hex\tfilename" : 464 " text\t data\t bss\t dec\t hex\tfilename"); 465#endif 466 467 total = textsize + datasize + bsssize; 468 469 if (show_totals) 470 { 471 total_textsize += textsize; 472 total_datasize += datasize; 473 total_bsssize += bsssize; 474 } 475 476 rprint_number (7, textsize); 477 putchar ('\t'); 478 rprint_number (7, datasize); 479 putchar ('\t'); 480 rprint_number (7, bsssize); 481 printf (((radix == octal) ? "\t%7lo\t%7lx\t" : "\t%7lu\t%7lx\t"), 482 (unsigned long) total, (unsigned long) total); 483 484 fputs (bfd_get_filename (abfd), stdout); 485 486 if (bfd_my_archive (abfd)) 487 printf (" (ex %s)", bfd_get_filename (bfd_my_archive (abfd))); 488} 489 490/* I REALLY miss lexical functions! */ 491bfd_size_type svi_total = 0; 492bfd_vma svi_maxvma = 0; 493int svi_namelen = 0; 494int svi_vmalen = 0; 495int svi_sizelen = 0; 496 497static void 498sysv_internal_sizer (file, sec, ignore) 499 bfd *file ATTRIBUTE_UNUSED; 500 sec_ptr sec; 501 PTR ignore ATTRIBUTE_UNUSED; 502{ 503 bfd_size_type size = bfd_section_size (file, sec); 504 505 if ( ! bfd_is_abs_section (sec) 506 && ! bfd_is_com_section (sec) 507 && ! bfd_is_und_section (sec)) 508 { 509 int namelen = strlen (bfd_section_name (file, sec)); 510 511 if (namelen > svi_namelen) 512 svi_namelen = namelen; 513 514 svi_total += size; 515 516 if (bfd_section_vma (file, sec) > svi_maxvma) 517 svi_maxvma = bfd_section_vma (file, sec); 518 } 519} 520 521static void 522sysv_internal_printer (file, sec, ignore) 523 bfd *file ATTRIBUTE_UNUSED; 524 sec_ptr sec; 525 PTR ignore ATTRIBUTE_UNUSED; 526{ 527 bfd_size_type size = bfd_section_size (file, sec); 528 529 if ( ! bfd_is_abs_section (sec) 530 && ! bfd_is_com_section (sec) 531 && ! bfd_is_und_section (sec)) 532 { 533 svi_total += size; 534 535 printf ("%-*s ", svi_namelen, bfd_section_name (file, sec)); 536 rprint_number (svi_sizelen, size); 537 printf (" "); 538 rprint_number (svi_vmalen, bfd_section_vma (file, sec)); 539 printf ("\n"); 540 } 541} 542 543static void 544print_sysv_format (file) 545 bfd *file; 546{ 547 /* Size all of the columns. */ 548 svi_total = 0; 549 svi_maxvma = 0; 550 svi_namelen = 0; 551 bfd_map_over_sections (file, sysv_internal_sizer, (PTR) NULL); 552 svi_vmalen = size_number ((bfd_size_type)svi_maxvma); 553 554 if ((size_t) svi_vmalen < sizeof ("addr") - 1) 555 svi_vmalen = sizeof ("addr")-1; 556 557 svi_sizelen = size_number (svi_total); 558 if ((size_t) svi_sizelen < sizeof ("size") - 1) 559 svi_sizelen = sizeof ("size")-1; 560 561 svi_total = 0; 562 printf ("%s ", bfd_get_filename (file)); 563 564 if (bfd_my_archive (file)) 565 printf (" (ex %s)", bfd_get_filename (bfd_my_archive (file))); 566 567 printf (":\n%-*s %*s %*s\n", svi_namelen, "section", 568 svi_sizelen, "size", svi_vmalen, "addr"); 569 570 bfd_map_over_sections (file, sysv_internal_printer, (PTR) NULL); 571 572 printf ("%-*s ", svi_namelen, "Total"); 573 rprint_number (svi_sizelen, svi_total); 574 printf ("\n\n"); 575} 576 577static void 578print_sizes (file) 579 bfd *file; 580{ 581 if (berkeley_format) 582 print_berkeley_format (file); 583 else 584 print_sysv_format (file); 585} 586