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