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