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