Back to index

cell-binutils  2.17cvs20070401
size.c
Go to the documentation of this file.
00001 /* size.c -- report size of various sections of an executable file.
00002    Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
00003    2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
00004 
00005    This file is part of GNU Binutils.
00006 
00007    This program is free software; you can redistribute it and/or modify
00008    it under the terms of the GNU General Public License as published by
00009    the Free Software Foundation; either version 2 of the License, or
00010    (at your option) any later version.
00011 
00012    This program is distributed in the hope that it will be useful,
00013    but WITHOUT ANY WARRANTY; without even the implied warranty of
00014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015    GNU General Public License for more details.
00016 
00017    You should have received a copy of the GNU General Public License
00018    along with this program; if not, write to the Free Software
00019    Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.  */
00020 
00021 /* Extensions/incompatibilities:
00022    o - BSD output has filenames at the end.
00023    o - BSD output can appear in different radicies.
00024    o - SysV output has less redundant whitespace.  Filename comes at end.
00025    o - SysV output doesn't show VMA which is always the same as the PMA.
00026    o - We also handle core files.
00027    o - We also handle archives.
00028    If you write shell scripts which manipulate this info then you may be
00029    out of luck; there's no --compatibility or --pedantic option.  */
00030 
00031 #include "bfd.h"
00032 #include "bucomm.h"
00033 #include "libiberty.h"
00034 #include "getopt.h"
00035 
00036 #ifndef BSD_DEFAULT
00037 #define BSD_DEFAULT 1
00038 #endif
00039 
00040 /* Program options.  */
00041 
00042 enum
00043   {
00044     decimal, octal, hex
00045   }
00046 radix = decimal;
00047 
00048 /* 0 means use AT&T-style output.  */
00049 static int berkeley_format = BSD_DEFAULT;
00050 
00051 int show_version = 0;
00052 int show_help = 0;
00053 int show_totals = 0;
00054 
00055 static bfd_size_type total_bsssize;
00056 static bfd_size_type total_datasize;
00057 static bfd_size_type total_textsize;
00058 
00059 /* Program exit status.  */
00060 int return_code = 0;
00061 
00062 static char *target = NULL;
00063 
00064 /* Static declarations.  */
00065 
00066 static void usage (FILE *, int);
00067 static void display_file (char *);
00068 static void display_bfd (bfd *);
00069 static void display_archive (bfd *);
00070 static int size_number (bfd_size_type);
00071 static void rprint_number (int, bfd_size_type);
00072 static void print_berkeley_format (bfd *);
00073 static void sysv_internal_sizer (bfd *, asection *, void *);
00074 static void sysv_internal_printer (bfd *, asection *, void *);
00075 static void print_sysv_format (bfd *);
00076 static void print_sizes (bfd * file);
00077 static void berkeley_sum (bfd *, sec_ptr, void *);
00078 
00079 static void
00080 usage (FILE *stream, int status)
00081 {
00082   fprintf (stream, _("Usage: %s [option(s)] [file(s)]\n"), program_name);
00083   fprintf (stream, _(" Displays the sizes of sections inside binary files\n"));
00084   fprintf (stream, _(" If no input file(s) are specified, a.out is assumed\n"));
00085   fprintf (stream, _(" The options are:\n\
00086   -A|-B     --format={sysv|berkeley}  Select output style (default is %s)\n\
00087   -o|-d|-x  --radix={8|10|16}         Display numbers in octal, decimal or hex\n\
00088   -t        --totals                  Display the total sizes (Berkeley only)\n\
00089             --target=<bfdname>        Set the binary file format\n\
00090             @<file>                   Read options from <file>\n\
00091   -h        --help                    Display this information\n\
00092   -v        --version                 Display the program's version\n\
00093 \n"),
00094 #if BSD_DEFAULT
00095   "berkeley"
00096 #else
00097   "sysv"
00098 #endif
00099 );
00100   list_supported_targets (program_name, stream);
00101   if (REPORT_BUGS_TO[0] && status == 0)
00102     fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
00103   exit (status);
00104 }
00105 
00106 static struct option long_options[] =
00107 {
00108   {"format", required_argument, 0, 200},
00109   {"radix", required_argument, 0, 201},
00110   {"target", required_argument, 0, 202},
00111   {"totals", no_argument, &show_totals, 1},
00112   {"version", no_argument, &show_version, 1},
00113   {"help", no_argument, &show_help, 1},
00114   {0, no_argument, 0, 0}
00115 };
00116 
00117 int main (int, char **);
00118 
00119 int
00120 main (int argc, char **argv)
00121 {
00122   int temp;
00123   int c;
00124 
00125 #if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
00126   setlocale (LC_MESSAGES, "");
00127 #endif
00128 #if defined (HAVE_SETLOCALE)
00129   setlocale (LC_CTYPE, "");
00130 #endif
00131   bindtextdomain (PACKAGE, LOCALEDIR);
00132   textdomain (PACKAGE);
00133 
00134   program_name = *argv;
00135   xmalloc_set_program_name (program_name);
00136 
00137   expandargv (&argc, &argv);
00138 
00139   bfd_init ();
00140   set_default_bfd_target ();
00141 
00142   while ((c = getopt_long (argc, argv, "ABHhVvdfotx", long_options,
00143                         (int *) 0)) != EOF)
00144     switch (c)
00145       {
00146       case 200:             /* --format */
00147        switch (*optarg)
00148          {
00149          case 'B':
00150          case 'b':
00151            berkeley_format = 1;
00152            break;
00153          case 'S':
00154          case 's':
00155            berkeley_format = 0;
00156            break;
00157          default:
00158            non_fatal (_("invalid argument to --format: %s"), optarg);
00159            usage (stderr, 1);
00160          }
00161        break;
00162 
00163       case 202:             /* --target */
00164        target = optarg;
00165        break;
00166 
00167       case 201:             /* --radix */
00168 #ifdef ANSI_LIBRARIES
00169        temp = strtol (optarg, NULL, 10);
00170 #else
00171        temp = atol (optarg);
00172 #endif
00173        switch (temp)
00174          {
00175          case 10:
00176            radix = decimal;
00177            break;
00178          case 8:
00179            radix = octal;
00180            break;
00181          case 16:
00182            radix = hex;
00183            break;
00184          default:
00185            non_fatal (_("Invalid radix: %s\n"), optarg);
00186            usage (stderr, 1);
00187          }
00188        break;
00189 
00190       case 'A':
00191        berkeley_format = 0;
00192        break;
00193       case 'B':
00194        berkeley_format = 1;
00195        break;
00196       case 'v':
00197       case 'V':
00198        show_version = 1;
00199        break;
00200       case 'd':
00201        radix = decimal;
00202        break;
00203       case 'x':
00204        radix = hex;
00205        break;
00206       case 'o':
00207        radix = octal;
00208        break;
00209       case 't':
00210        show_totals = 1;
00211        break;
00212       case 'f': /* FIXME : For sysv68, `-f' means `full format', i.e.
00213                  `[fname:] M(.text) + N(.data) + O(.bss) + P(.comment) = Q'
00214                  where `fname: ' appears only if there are >= 2 input files,
00215                  and M, N, O, P, Q are expressed in decimal by default,
00216                  hexa or octal if requested by `-x' or `-o'.
00217                  Just to make things interesting, Solaris also accepts -f,
00218                  which prints out the size of each allocatable section, the
00219                  name of the section, and the total of the section sizes.  */
00220               /* For the moment, accept `-f' silently, and ignore it.  */
00221        break;
00222       case 0:
00223        break;
00224       case 'h':
00225       case 'H':
00226       case '?':
00227        usage (stderr, 1);
00228       }
00229 
00230   if (show_version)
00231     print_version ("size");
00232   if (show_help)
00233     usage (stdout, 0);
00234 
00235   if (optind == argc)
00236     display_file ("a.out");
00237   else
00238     for (; optind < argc;)
00239       display_file (argv[optind++]);
00240 
00241   if (show_totals && berkeley_format)
00242     {
00243       bfd_size_type total = total_textsize + total_datasize + total_bsssize;
00244 
00245       rprint_number (7, total_textsize);
00246       putchar('\t');
00247       rprint_number (7, total_datasize);
00248       putchar('\t');
00249       rprint_number (7, total_bsssize);
00250       printf (((radix == octal) ? "\t%7lo\t%7lx\t" : "\t%7lu\t%7lx\t"),
00251              (unsigned long) total, (unsigned long) total);
00252       fputs ("(TOTALS)\n", stdout);
00253     }
00254 
00255   return return_code;
00256 }
00257 
00258 /* Display stats on file or archive member ABFD.  */
00259 
00260 static void
00261 display_bfd (bfd *abfd)
00262 {
00263   char **matching;
00264 
00265   if (bfd_check_format (abfd, bfd_archive))
00266     /* An archive within an archive.  */
00267     return;
00268 
00269   if (bfd_check_format_matches (abfd, bfd_object, &matching))
00270     {
00271       print_sizes (abfd);
00272       printf ("\n");
00273       return;
00274     }
00275 
00276   if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
00277     {
00278       bfd_nonfatal (bfd_get_filename (abfd));
00279       list_matching_formats (matching);
00280       free (matching);
00281       return_code = 3;
00282       return;
00283     }
00284 
00285   if (bfd_check_format_matches (abfd, bfd_core, &matching))
00286     {
00287       const char *core_cmd;
00288 
00289       print_sizes (abfd);
00290       fputs (" (core file", stdout);
00291 
00292       core_cmd = bfd_core_file_failing_command (abfd);
00293       if (core_cmd)
00294        printf (" invoked as %s", core_cmd);
00295 
00296       puts (")\n");
00297       return;
00298     }
00299 
00300   bfd_nonfatal (bfd_get_filename (abfd));
00301 
00302   if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
00303     {
00304       list_matching_formats (matching);
00305       free (matching);
00306     }
00307 
00308   return_code = 3;
00309 }
00310 
00311 static void
00312 display_archive (bfd *file)
00313 {
00314   bfd *arfile = (bfd *) NULL;
00315   bfd *last_arfile = (bfd *) NULL;
00316 
00317   for (;;)
00318     {
00319       bfd_set_error (bfd_error_no_error);
00320 
00321       arfile = bfd_openr_next_archived_file (file, arfile);
00322       if (arfile == NULL)
00323        {
00324          if (bfd_get_error () != bfd_error_no_more_archived_files)
00325            {
00326              bfd_nonfatal (bfd_get_filename (file));
00327              return_code = 2;
00328            }
00329          break;
00330        }
00331 
00332       display_bfd (arfile);
00333 
00334       if (last_arfile != NULL)
00335        bfd_close (last_arfile);
00336       last_arfile = arfile;
00337     }
00338 
00339   if (last_arfile != NULL)
00340     bfd_close (last_arfile);
00341 }
00342 
00343 static void
00344 display_file (char *filename)
00345 {
00346   bfd *file;
00347 
00348   if (get_file_size (filename) < 1)
00349     {
00350       return_code = 1;
00351       return;
00352     }
00353 
00354   file = bfd_openr (filename, target);
00355   if (file == NULL)
00356     {
00357       bfd_nonfatal (filename);
00358       return_code = 1;
00359       return;
00360     }
00361 
00362   if (bfd_check_format (file, bfd_archive))
00363     display_archive (file);
00364   else
00365     display_bfd (file);
00366 
00367   if (!bfd_close (file))
00368     {
00369       bfd_nonfatal (filename);
00370       return_code = 1;
00371       return;
00372     }
00373 }
00374 
00375 /* This is what lexical functions are for.  */
00376 
00377 static int
00378 size_number (bfd_size_type num)
00379 {
00380   char buffer[40];
00381 
00382   sprintf (buffer,
00383           (radix == decimal ? "%lu" :
00384           ((radix == octal) ? "0%lo" : "0x%lx")),
00385           (unsigned long) num);
00386 
00387   return strlen (buffer);
00388 }
00389 
00390 static void
00391 rprint_number (int width, bfd_size_type num)
00392 {
00393   char buffer[40];
00394 
00395   sprintf (buffer,
00396           (radix == decimal ? "%lu" :
00397           ((radix == octal) ? "0%lo" : "0x%lx")),
00398           (unsigned long) num);
00399 
00400   printf ("%*s", width, buffer);
00401 }
00402 
00403 static bfd_size_type bsssize;
00404 static bfd_size_type datasize;
00405 static bfd_size_type textsize;
00406 
00407 static void
00408 berkeley_sum (bfd *abfd ATTRIBUTE_UNUSED, sec_ptr sec,
00409              void *ignore ATTRIBUTE_UNUSED)
00410 {
00411   flagword flags;
00412   bfd_size_type size;
00413 
00414   flags = bfd_get_section_flags (abfd, sec);
00415   if ((flags & SEC_ALLOC) == 0)
00416     return;
00417 
00418   size = bfd_get_section_size (sec);
00419   if ((flags & SEC_CODE) != 0 || (flags & SEC_READONLY) != 0)
00420     textsize += size;
00421   else if ((flags & SEC_HAS_CONTENTS) != 0)
00422     datasize += size;
00423   else
00424     bsssize += size;
00425 }
00426 
00427 static void
00428 print_berkeley_format (bfd *abfd)
00429 {
00430   static int files_seen = 0;
00431   bfd_size_type total;
00432 
00433   bsssize = 0;
00434   datasize = 0;
00435   textsize = 0;
00436 
00437   bfd_map_over_sections (abfd, berkeley_sum, NULL);
00438 
00439   if (files_seen++ == 0)
00440     puts ((radix == octal) ? "   text\t   data\t    bss\t    oct\t    hex\tfilename" :
00441          "   text\t   data\t    bss\t    dec\t    hex\tfilename");
00442 
00443   total = textsize + datasize + bsssize;
00444 
00445   if (show_totals)
00446     {
00447       total_textsize += textsize;
00448       total_datasize += datasize;
00449       total_bsssize  += bsssize;
00450     }
00451 
00452   rprint_number (7, textsize);
00453   putchar ('\t');
00454   rprint_number (7, datasize);
00455   putchar ('\t');
00456   rprint_number (7, bsssize);
00457   printf (((radix == octal) ? "\t%7lo\t%7lx\t" : "\t%7lu\t%7lx\t"),
00458          (unsigned long) total, (unsigned long) total);
00459 
00460   fputs (bfd_get_filename (abfd), stdout);
00461 
00462   if (bfd_my_archive (abfd))
00463     printf (" (ex %s)", bfd_get_filename (bfd_my_archive (abfd)));
00464 }
00465 
00466 /* I REALLY miss lexical functions! */
00467 bfd_size_type svi_total = 0;
00468 bfd_vma svi_maxvma = 0;
00469 int svi_namelen = 0;
00470 int svi_vmalen = 0;
00471 int svi_sizelen = 0;
00472 
00473 static void
00474 sysv_internal_sizer (bfd *file ATTRIBUTE_UNUSED, sec_ptr sec,
00475                    void *ignore ATTRIBUTE_UNUSED)
00476 {
00477   bfd_size_type size = bfd_section_size (file, sec);
00478 
00479   if (   ! bfd_is_abs_section (sec)
00480       && ! bfd_is_com_section (sec)
00481       && ! bfd_is_und_section (sec))
00482     {
00483       int namelen = strlen (bfd_section_name (file, sec));
00484 
00485       if (namelen > svi_namelen)
00486        svi_namelen = namelen;
00487 
00488       svi_total += size;
00489 
00490       if (bfd_section_vma (file, sec) > svi_maxvma)
00491        svi_maxvma = bfd_section_vma (file, sec);
00492     }
00493 }
00494 
00495 static void
00496 sysv_internal_printer (bfd *file ATTRIBUTE_UNUSED, sec_ptr sec,
00497                      void *ignore ATTRIBUTE_UNUSED)
00498 {
00499   bfd_size_type size = bfd_section_size (file, sec);
00500 
00501   if (   ! bfd_is_abs_section (sec)
00502       && ! bfd_is_com_section (sec)
00503       && ! bfd_is_und_section (sec))
00504     {
00505       svi_total += size;
00506 
00507       printf ("%-*s   ", svi_namelen, bfd_section_name (file, sec));
00508       rprint_number (svi_sizelen, size);
00509       printf ("   ");
00510       rprint_number (svi_vmalen, bfd_section_vma (file, sec));
00511       printf ("\n");
00512     }
00513 }
00514 
00515 static void
00516 print_sysv_format (bfd *file)
00517 {
00518   /* Size all of the columns.  */
00519   svi_total = 0;
00520   svi_maxvma = 0;
00521   svi_namelen = 0;
00522   bfd_map_over_sections (file, sysv_internal_sizer, NULL);
00523   svi_vmalen = size_number ((bfd_size_type)svi_maxvma);
00524 
00525   if ((size_t) svi_vmalen < sizeof ("addr") - 1)
00526     svi_vmalen = sizeof ("addr")-1;
00527 
00528   svi_sizelen = size_number (svi_total);
00529   if ((size_t) svi_sizelen < sizeof ("size") - 1)
00530     svi_sizelen = sizeof ("size")-1;
00531 
00532   svi_total = 0;
00533   printf ("%s  ", bfd_get_filename (file));
00534 
00535   if (bfd_my_archive (file))
00536     printf (" (ex %s)", bfd_get_filename (bfd_my_archive (file)));
00537 
00538   printf (":\n%-*s   %*s   %*s\n", svi_namelen, "section",
00539          svi_sizelen, "size", svi_vmalen, "addr");
00540 
00541   bfd_map_over_sections (file, sysv_internal_printer, NULL);
00542 
00543   printf ("%-*s   ", svi_namelen, "Total");
00544   rprint_number (svi_sizelen, svi_total);
00545   printf ("\n\n");
00546 }
00547 
00548 static void
00549 print_sizes (bfd *file)
00550 {
00551   if (berkeley_format)
00552     print_berkeley_format (file);
00553   else
00554     print_sysv_format (file);
00555 }