size.c revision 77298
16295Siignatyev/* size.c -- report size of various sections of an executable file.
211833Sctornqvi   Copyright 1991, 92, 93, 94, 95, 96, 97, 98, 99, 2000
36295Siignatyev   Free Software Foundation, Inc.
46295Siignatyev
56295SiignatyevThis file is part of GNU Binutils.
66295Siignatyev
76295SiignatyevThis program is free software; you can redistribute it and/or modify
86295Siignatyevit under the terms of the GNU General Public License as published by
96295Siignatyevthe Free Software Foundation; either version 2 of the License, or
106295Siignatyev(at your option) any later version.
116295Siignatyev
126295SiignatyevThis program is distributed in the hope that it will be useful,
136295Siignatyevbut WITHOUT ANY WARRANTY; without even the implied warranty of
146295SiignatyevMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
156295SiignatyevGNU General Public License for more details.
166295Siignatyev
176295SiignatyevYou should have received a copy of the GNU General Public License
186295Siignatyevalong with this program; if not, write to the Free Software
196295SiignatyevFoundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
206295Siignatyev
216295Siignatyev/* Extensions/incompatibilities:
226295Siignatyev   o - BSD output has filenames at the end.
236295Siignatyev   o - BSD output can appear in different radicies.
246295Siignatyev   o - SysV output has less redundant whitespace.  Filename comes at end.
256295Siignatyev   o - SysV output doesn't show VMA which is always the same as the PMA.
266295Siignatyev   o - We also handle core files.
276295Siignatyev   o - We also handle archives.
2811833Sctornqvi   If you write shell scripts which manipulate this info then you may be
2910551Schegar   out of luck; there's no --compatibility or --pedantic option.
308013Sykantser*/
3113139Sepavlova
3211833Sctornqvi#include "bfd.h"
3311534Siignatyev#include "getopt.h"
3411534Siignatyev#include "bucomm.h"
356295Siignatyev#include "libiberty.h"
3611707Stpivovarova
3711707Stpivovarova#ifndef BSD_DEFAULT
386295Siignatyev#define BSD_DEFAULT 1
396295Siignatyev#endif
4011707Stpivovarova
4111707Stpivovarova/* Program options.  */
4211707Stpivovarova
4311707Stpivovarovaenum
4411707Stpivovarova  {
4511707Stpivovarova    decimal, octal, hex
4611707Stpivovarova  } radix = decimal;
4711833Sctornqviint berkeley_format = BSD_DEFAULT;	/* 0 means use AT&T-style output.  */
4811707Stpivovarovaint show_version = 0;
496295Siignatyevint show_help = 0;
506295Siignatyev
516295Siignatyev/* Program exit status.  */
526295Siignatyevint return_code = 0;
536295Siignatyev
546295Siignatyevstatic char *target = NULL;
556295Siignatyev
566295Siignatyev/* Static declarations */
576295Siignatyev
586295Siignatyevstatic void usage PARAMS ((FILE *, int));
596295Siignatyevstatic void display_file PARAMS ((char *filename));
606295Siignatyevstatic void display_bfd PARAMS ((bfd *));
6113139Sepavlovastatic void display_archive PARAMS ((bfd *));
626295Siignatyevstatic int size_number PARAMS ((bfd_size_type));
636295Siignatyev#if 0
646295Siignatyevstatic void lprint_number PARAMS ((int, bfd_size_type));
656295Siignatyev#endif
666295Siignatyevstatic void rprint_number PARAMS ((int, bfd_size_type));
676295Siignatyevstatic void print_berkeley_format PARAMS ((bfd *));
686295Siignatyevstatic void sysv_internal_sizer PARAMS ((bfd *, asection *, PTR));
696295Siignatyevstatic void sysv_internal_printer PARAMS ((bfd *, asection *, PTR));
706295Siignatyevstatic void print_sysv_format PARAMS ((bfd *));
716295Siignatyevstatic void print_sizes PARAMS ((bfd * file));
726295Siignatyevstatic void berkeley_sum PARAMS ((bfd *, sec_ptr, PTR));
736295Siignatyev
746295Siignatyevstatic void
756295Siignatyevusage (stream, status)
766295Siignatyev     FILE *stream;
776295Siignatyev     int status;
786295Siignatyev{
796295Siignatyev  fprintf (stream, _("\
806295SiignatyevUsage: %s [-A | --format=sysv | -B | --format=berkeley]\n\
816295Siignatyev       [-o | --radix=8 | -d | --radix=10 | -h | --radix=16]\n\
826295Siignatyev       [-V | --version] [--target=bfdname] [--help] [file...]\n"),
836295Siignatyev	   program_name);
846295Siignatyev#if BSD_DEFAULT
856295Siignatyev  fputs (_("default is --format=berkeley\n"), stream);
866295Siignatyev#else
876295Siignatyev  fputs (_("default is --format=sysv\n"), stream);
886295Siignatyev#endif
896295Siignatyev  list_supported_targets (program_name, stream);
906295Siignatyev  if (status == 0)
916295Siignatyev    fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO);
926295Siignatyev  exit (status);
936295Siignatyev}
9413139Sepavlova
956295Siignatyevstruct option long_options[] =
966295Siignatyev{
97  {"format", required_argument, 0, 200},
98  {"radix", required_argument, 0, 201},
99  {"target", required_argument, 0, 202},
100  {"version", no_argument, &show_version, 1},
101  {"help", no_argument, &show_help, 1},
102  {0, no_argument, 0, 0}
103};
104
105int
106main (argc, argv)
107     int argc;
108     char **argv;
109{
110  int temp;
111  int c;
112
113#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES)
114  setlocale (LC_MESSAGES, "");
115#endif
116  bindtextdomain (PACKAGE, LOCALEDIR);
117  textdomain (PACKAGE);
118
119  program_name = *argv;
120  xmalloc_set_program_name (program_name);
121
122  bfd_init ();
123  set_default_bfd_target ();
124
125  while ((c = getopt_long (argc, argv, "ABVdfox", long_options,
126			   (int *) 0)) != EOF)
127    switch (c)
128      {
129      case 200:		/* --format */
130	switch (*optarg)
131	  {
132	  case 'B':
133	  case 'b':
134	    berkeley_format = 1;
135	    break;
136	  case 'S':
137	  case 's':
138	    berkeley_format = 0;
139	    break;
140	  default:
141	    non_fatal (_("invalid argument to --format: %s"), optarg);
142	    usage (stderr, 1);
143	  }
144	break;
145
146      case 202:		/* --target */
147	target = optarg;
148	break;
149
150      case 201:		/* --radix */
151#ifdef ANSI_LIBRARIES
152	temp = strtol (optarg, NULL, 10);
153#else
154	temp = atol (optarg);
155#endif
156	switch (temp)
157	  {
158	  case 10:
159	    radix = decimal;
160	    break;
161	  case 8:
162	    radix = octal;
163	    break;
164	  case 16:
165	    radix = hex;
166	    break;
167	  default:
168	    non_fatal (_("Invalid radix: %s\n"), optarg);
169	    usage (stderr, 1);
170	  }
171	break;
172
173      case 'A':
174	berkeley_format = 0;
175	break;
176      case 'B':
177	berkeley_format = 1;
178	break;
179      case 'V':
180	show_version = 1;
181	break;
182      case 'd':
183	radix = decimal;
184	break;
185      case 'x':
186	radix = hex;
187	break;
188      case 'o':
189	radix = octal;
190	break;
191      case 'f': /* FIXME : For sysv68, `-f' means `full format', i.e.
192		   `[fname:] M(.text) + N(.data) + O(.bss) + P(.comment) = Q'
193		   where `fname: ' appears only if there are >= 2 input files,
194		   and M, N, O, P, Q are expressed in decimal by default,
195		   hexa or octal if requested by `-x' or `-o'.
196		   Just to make things interesting, Solaris also accepts -f,
197		   which prints out the size of each allocatable section, the
198		   name of the section, and the total of the section sizes.  */
199		/* For the moment, accept `-f' silently, and ignore it.  */
200	break;
201      case 0:
202	break;
203      case '?':
204	usage (stderr, 1);
205      }
206
207  if (show_version)
208    print_version ("size");
209  if (show_help)
210    usage (stdout, 0);
211
212  if (optind == argc)
213    display_file ("a.out");
214  else
215    for (; optind < argc;)
216      display_file (argv[optind++]);
217
218  return return_code;
219}
220
221/* Display stats on file or archive member ABFD.  */
222
223static void
224display_bfd (abfd)
225     bfd *abfd;
226{
227  char **matching;
228
229  if (bfd_check_format (abfd, bfd_archive))
230    /* An archive within an archive.  */
231    return;
232
233  if (bfd_check_format_matches (abfd, bfd_object, &matching))
234    {
235      print_sizes (abfd);
236      printf ("\n");
237      return;
238    }
239
240  if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
241    {
242      bfd_nonfatal (bfd_get_filename (abfd));
243      list_matching_formats (matching);
244      free (matching);
245      return_code = 3;
246      return;
247    }
248
249  if (bfd_check_format_matches (abfd, bfd_core, &matching))
250    {
251      CONST char *core_cmd;
252
253      print_sizes (abfd);
254      fputs (" (core file", stdout);
255
256      core_cmd = bfd_core_file_failing_command (abfd);
257      if (core_cmd)
258	printf (" invoked as %s", core_cmd);
259
260      puts (")\n");
261      return;
262    }
263
264  bfd_nonfatal (bfd_get_filename (abfd));
265
266  if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
267    {
268      list_matching_formats (matching);
269      free (matching);
270    }
271
272  return_code = 3;
273}
274
275static void
276display_archive (file)
277     bfd *file;
278{
279  bfd *arfile = (bfd *) NULL;
280
281  for (;;)
282    {
283      bfd_set_error (bfd_error_no_error);
284
285      arfile = bfd_openr_next_archived_file (file, arfile);
286      if (arfile == NULL)
287	{
288	  if (bfd_get_error () != bfd_error_no_more_archived_files)
289	    {
290	      bfd_nonfatal (bfd_get_filename (file));
291	      return_code = 2;
292	    }
293	  break;
294	}
295
296      display_bfd (arfile);
297      /* Don't close the archive elements; we need them for next_archive */
298    }
299}
300
301static void
302display_file (filename)
303     char *filename;
304{
305  bfd *file = bfd_openr (filename, target);
306  if (file == NULL)
307    {
308      bfd_nonfatal (filename);
309      return_code = 1;
310      return;
311    }
312
313  if (bfd_check_format (file, bfd_archive) == true)
314    display_archive (file);
315  else
316    display_bfd (file);
317
318  if (bfd_close (file) == false)
319    {
320      bfd_nonfatal (filename);
321      return_code = 1;
322      return;
323    }
324}
325
326/* This is what lexical functions are for.  */
327
328static int
329size_number (num)
330     bfd_size_type num;
331{
332  char buffer[40];
333  sprintf (buffer,
334	   (radix == decimal ? "%lu" :
335	   ((radix == octal) ? "0%lo" : "0x%lx")),
336	   (unsigned long) num);
337
338  return strlen (buffer);
339}
340
341#if 0
342
343/* This is not used.  */
344
345static void
346lprint_number (width, num)
347     int width;
348     bfd_size_type num;
349{
350  char buffer[40];
351  sprintf (buffer,
352	   (radix == decimal ? "%lu" :
353	   ((radix == octal) ? "0%lo" : "0x%lx")),
354	   (unsigned long) num);
355
356  printf ("%-*s", width, buffer);
357}
358
359#endif
360
361static void
362rprint_number (width, num)
363     int width;
364     bfd_size_type num;
365{
366  char buffer[40];
367  sprintf (buffer,
368	   (radix == decimal ? "%lu" :
369	   ((radix == octal) ? "0%lo" : "0x%lx")),
370	   (unsigned long) num);
371
372  printf ("%*s", width, buffer);
373}
374
375static bfd_size_type bsssize;
376static bfd_size_type datasize;
377static bfd_size_type textsize;
378
379static void
380berkeley_sum (abfd, sec, ignore)
381     bfd *abfd ATTRIBUTE_UNUSED;
382     sec_ptr sec;
383     PTR ignore ATTRIBUTE_UNUSED;
384{
385  flagword flags;
386  bfd_size_type size;
387
388  flags = bfd_get_section_flags (abfd, sec);
389  if ((flags & SEC_ALLOC) == 0)
390    return;
391
392  size = bfd_get_section_size_before_reloc (sec);
393  if ((flags & SEC_CODE) != 0 || (flags & SEC_READONLY) != 0)
394    textsize += size;
395  else if ((flags & SEC_HAS_CONTENTS) != 0)
396    datasize += size;
397  else
398    bsssize += size;
399}
400
401static void
402print_berkeley_format (abfd)
403     bfd *abfd;
404{
405  static int files_seen = 0;
406  bfd_size_type total;
407
408  bsssize = 0;
409  datasize = 0;
410  textsize = 0;
411
412  bfd_map_over_sections (abfd, berkeley_sum, (PTR) NULL);
413
414  if (files_seen++ == 0)
415#if 0
416    /* Intel doesn't like bss/stk because they don't have core files.  */
417    puts ((radix == octal) ? "   text\t   data\tbss/stk\t    oct\t    hex\tfilename" :
418	  "   text\t   data\tbss/stk\t    dec\t    hex\tfilename");
419#else
420    puts ((radix == octal) ? "   text\t   data\t    bss\t    oct\t    hex\tfilename" :
421	  "   text\t   data\t    bss\t    dec\t    hex\tfilename");
422#endif
423
424  total = textsize + datasize + bsssize;
425
426  rprint_number (7, textsize);
427  putchar ('\t');
428  rprint_number (7, datasize);
429  putchar ('\t');
430  rprint_number (7, bsssize);
431  printf (((radix == octal) ? "\t%7lo\t%7lx\t" : "\t%7lu\t%7lx\t"),
432	  (unsigned long) total, (unsigned long) total);
433
434  fputs (bfd_get_filename (abfd), stdout);
435  if (bfd_my_archive (abfd))
436    printf (" (ex %s)", bfd_get_filename (bfd_my_archive (abfd)));
437}
438
439/* I REALLY miss lexical functions! */
440bfd_size_type svi_total = 0;
441bfd_vma svi_maxvma = 0;
442int svi_namelen = 0;
443int svi_vmalen = 0;
444int svi_sizelen = 0;
445
446static void
447sysv_internal_sizer (file, sec, ignore)
448     bfd *file ATTRIBUTE_UNUSED;
449     sec_ptr sec;
450     PTR ignore ATTRIBUTE_UNUSED;
451{
452  bfd_size_type size = bfd_section_size (file, sec);
453  if (!bfd_is_abs_section (sec)
454      && !bfd_is_com_section (sec)
455      && !bfd_is_und_section (sec))
456    {
457      int namelen = strlen (bfd_section_name (file, sec));
458      if (namelen > svi_namelen)
459	svi_namelen = namelen;
460
461      svi_total += size;
462      if (bfd_section_vma (file, sec) > svi_maxvma)
463	svi_maxvma = bfd_section_vma (file, sec);
464    }
465}
466
467static void
468sysv_internal_printer (file, sec, ignore)
469     bfd *file ATTRIBUTE_UNUSED;
470     sec_ptr sec;
471     PTR ignore ATTRIBUTE_UNUSED;
472{
473  bfd_size_type size = bfd_section_size (file, sec);
474  if (!bfd_is_abs_section (sec)
475      && !bfd_is_com_section (sec)
476      && !bfd_is_und_section (sec))
477    {
478      svi_total += size;
479
480      printf ("%-*s   ", svi_namelen, bfd_section_name (file, sec));
481      rprint_number (svi_sizelen, size);
482      printf ("   ");
483      rprint_number (svi_vmalen, bfd_section_vma (file, sec));
484      printf ("\n");
485    }
486}
487
488static void
489print_sysv_format (file)
490     bfd *file;
491{
492  /* size all of the columns */
493  svi_total = 0;
494  svi_maxvma = 0;
495  svi_namelen = 0;
496  bfd_map_over_sections (file, sysv_internal_sizer, (PTR) NULL);
497  svi_vmalen = size_number ((bfd_size_type)svi_maxvma);
498  if ((size_t) svi_vmalen < sizeof ("addr") - 1)
499    svi_vmalen = sizeof ("addr")-1;
500
501  svi_sizelen = size_number (svi_total);
502  if ((size_t) svi_sizelen < sizeof ("size") - 1)
503    svi_sizelen = sizeof ("size")-1;
504
505  svi_total = 0;
506  printf ("%s  ", bfd_get_filename (file));
507  if (bfd_my_archive (file))
508    printf (" (ex %s)", bfd_get_filename (bfd_my_archive (file)));
509
510  printf (":\n%-*s   %*s   %*s\n", svi_namelen, "section",
511	  svi_sizelen, "size", svi_vmalen, "addr");
512  bfd_map_over_sections (file, sysv_internal_printer, (PTR) NULL);
513
514  printf ("%-*s   ", svi_namelen, "Total");
515  rprint_number (svi_sizelen, svi_total);
516  printf ("\n\n");
517}
518
519static void
520print_sizes (file)
521     bfd *file;
522{
523  if (berkeley_format)
524    print_berkeley_format (file);
525  else
526    print_sysv_format (file);
527}
528