size.c revision 33965
1/* size.c -- report size of various sections of an executable file.
2   Copyright 1991, 92, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
3
4This file is part of GNU Binutils.
5
6This program is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2 of the License, or
9(at your option) any later version.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with this program; if not, write to the Free Software
18Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19
20/* Extensions/incompatibilities:
21   o - BSD output has filenames at the end.
22   o - BSD output can appear in different radicies.
23   o - SysV output has less redundant whitespace.  Filename comes at end.
24   o - SysV output doesn't show VMA which is always the same as the PMA.
25   o - We also handle core files.
26   o - We also handle archives.
27   If you write shell scripts which manipulate this info then you may be
28   out of luck; there's no --compatibility or --pedantic option.
29*/
30
31#include "bfd.h"
32#include "getopt.h"
33#include "bucomm.h"
34#include "libiberty.h"
35
36#ifndef BSD_DEFAULT
37#define BSD_DEFAULT 1
38#endif
39
40/* Program options.  */
41
42enum
43  {
44    decimal, octal, hex
45  } radix = decimal;
46int berkeley_format = BSD_DEFAULT;	/* 0 means use AT&T-style output.  */
47int show_version = 0;
48int show_help = 0;
49
50/* Program exit status.  */
51int return_code = 0;
52
53/* IMPORTS */
54extern char *target;
55
56/* Static declarations */
57
58static void usage PARAMS ((FILE *, int));
59static void display_file PARAMS ((char *filename));
60static void display_bfd PARAMS ((bfd *));
61static void display_archive PARAMS ((bfd *));
62static void lprint_number PARAMS ((int, bfd_size_type));
63static void rprint_number PARAMS ((int, bfd_size_type));
64static void print_berkeley_format PARAMS ((bfd *));
65static void sysv_internal_printer PARAMS ((bfd *, asection *, PTR));
66static void print_sysv_format PARAMS ((bfd *));
67static void print_sizes PARAMS ((bfd * file));
68static void berkeley_sum PARAMS ((bfd *, sec_ptr, PTR));
69
70static void
71usage (stream, status)
72     FILE *stream;
73     int status;
74{
75  fprintf (stream, "\
76Usage: %s [-ABdoxV] [--format=berkeley|sysv] [--radix=8|10|16]\n\
77       [--target=bfdname] [--version] [--help] [file...]\n", program_name);
78#if BSD_DEFAULT
79  fputs ("default is --format=berkeley\n", stream);
80#else
81  fputs ("default is --format=sysv\n", stream);
82#endif
83  list_supported_targets (program_name, stream);
84  if (status == 0)
85    fprintf (stream, "Report bugs to bug-gnu-utils@prep.ai.mit.edu\n");
86  exit (status);
87}
88
89struct option long_options[] =
90{
91  {"format", required_argument, 0, 200},
92  {"radix", required_argument, 0, 201},
93  {"target", required_argument, 0, 202},
94  {"version", no_argument, &show_version, 1},
95  {"help", no_argument, &show_help, 1},
96  {0, no_argument, 0, 0}
97};
98
99int
100main (argc, argv)
101     int argc;
102     char **argv;
103{
104  int temp;
105  int c;
106
107  program_name = *argv;
108  xmalloc_set_program_name (program_name);
109
110  bfd_init ();
111  set_default_bfd_target ();
112
113  while ((c = getopt_long (argc, argv, "ABVdox", long_options,
114			   (int *) 0)) != EOF)
115    switch (c)
116      {
117      case 200:		/* --format */
118	switch (*optarg)
119	  {
120	  case 'B':
121	  case 'b':
122	    berkeley_format = 1;
123	    break;
124	  case 'S':
125	  case 's':
126	    berkeley_format = 0;
127	    break;
128	  default:
129	    fprintf (stderr, "invalid argument to --format: %s\n", optarg);
130	    usage (stderr, 1);
131	  }
132	break;
133
134      case 202:		/* --target */
135	target = optarg;
136	break;
137
138      case 201:		/* --radix */
139#ifdef ANSI_LIBRARIES
140	temp = strtol (optarg, NULL, 10);
141#else
142	temp = atol (optarg);
143#endif
144	switch (temp)
145	  {
146	  case 10:
147	    radix = decimal;
148	    break;
149	  case 8:
150	    radix = octal;
151	    break;
152	  case 16:
153	    radix = hex;
154	    break;
155	  default:
156	    printf ("Invalid radix: %s\n", optarg);
157	    usage (stderr, 1);
158	  }
159	break;
160
161      case 'A':
162	berkeley_format = 0;
163	break;
164      case 'B':
165	berkeley_format = 1;
166	break;
167      case 'V':
168	show_version = 1;
169	break;
170      case 'd':
171	radix = decimal;
172	break;
173      case 'x':
174	radix = hex;
175	break;
176      case 'o':
177	radix = octal;
178	break;
179      case 0:
180	break;
181      case '?':
182	usage (stderr, 1);
183      }
184
185  if (show_version)
186    print_version ("size");
187  if (show_help)
188    usage (stdout, 0);
189
190  if (optind == argc)
191    display_file ("a.out");
192  else
193    for (; optind < argc;)
194      display_file (argv[optind++]);
195
196  return return_code;
197}
198
199/* Display stats on file or archive member ABFD.  */
200
201static void
202display_bfd (abfd)
203     bfd *abfd;
204{
205  char **matching;
206
207  if (bfd_check_format (abfd, bfd_archive))
208    /* An archive within an archive.  */
209    return;
210
211  if (bfd_check_format_matches (abfd, bfd_object, &matching))
212    {
213      print_sizes (abfd);
214      printf ("\n");
215      return;
216    }
217
218  if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
219    {
220      bfd_nonfatal (bfd_get_filename (abfd));
221      list_matching_formats (matching);
222      free (matching);
223      return_code = 3;
224      return;
225    }
226
227  if (bfd_check_format_matches (abfd, bfd_core, &matching))
228    {
229      CONST char *core_cmd;
230
231      print_sizes (abfd);
232      fputs (" (core file", stdout);
233
234      core_cmd = bfd_core_file_failing_command (abfd);
235      if (core_cmd)
236	printf (" invoked as %s", core_cmd);
237
238      puts (")\n");
239      return;
240    }
241
242  bfd_nonfatal (bfd_get_filename (abfd));
243
244  if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
245    {
246      list_matching_formats (matching);
247      free (matching);
248    }
249
250  return_code = 3;
251}
252
253static void
254display_archive (file)
255     bfd *file;
256{
257  bfd *arfile = (bfd *) NULL;
258
259  for (;;)
260    {
261      bfd_set_error (bfd_error_no_error);
262
263      arfile = bfd_openr_next_archived_file (file, arfile);
264      if (arfile == NULL)
265	{
266	  if (bfd_get_error () != bfd_error_no_more_archived_files)
267	    {
268	      bfd_nonfatal (bfd_get_filename (file));
269	      return_code = 2;
270	    }
271	  break;
272	}
273
274      display_bfd (arfile);
275      /* Don't close the archive elements; we need them for next_archive */
276    }
277}
278
279static void
280display_file (filename)
281     char *filename;
282{
283  bfd *file = bfd_openr (filename, target);
284  if (file == NULL)
285    {
286      bfd_nonfatal (filename);
287      return_code = 1;
288      return;
289    }
290
291  if (bfd_check_format (file, bfd_archive) == true)
292    display_archive (file);
293  else
294    display_bfd (file);
295
296  if (bfd_close (file) == false)
297    {
298      bfd_nonfatal (filename);
299      return_code = 1;
300      return;
301    }
302}
303
304/* This is what lexical functions are for.  */
305
306static void
307lprint_number (width, num)
308     int width;
309     bfd_size_type num;
310{
311  printf ((radix == decimal ? "%-*lu\t" :
312	   ((radix == octal) ? "%-*lo\t" : "%-*lx\t")),
313	  width, (unsigned long) num);
314}
315
316static void
317rprint_number (width, num)
318     int width;
319     bfd_size_type num;
320{
321  printf ((radix == decimal ? "%*lu\t" :
322	   ((radix == octal) ? "%*lo\t" : "%*lx\t")),
323	  width, (unsigned long) num);
324}
325
326static bfd_size_type bsssize;
327static bfd_size_type datasize;
328static bfd_size_type textsize;
329
330static void
331berkeley_sum (abfd, sec, ignore)
332     bfd *abfd;
333     sec_ptr sec;
334     PTR ignore;
335{
336  flagword flags;
337  bfd_size_type size;
338
339  flags = bfd_get_section_flags (abfd, sec);
340  if ((flags & SEC_ALLOC) == 0)
341    return;
342
343  size = bfd_get_section_size_before_reloc (sec);
344  if ((flags & SEC_CODE) != 0 || (flags & SEC_READONLY) != 0)
345    textsize += size;
346  else if ((flags & SEC_HAS_CONTENTS) != 0)
347    datasize += size;
348  else
349    bsssize += size;
350}
351
352static void
353print_berkeley_format (abfd)
354     bfd *abfd;
355{
356  static int files_seen = 0;
357  bfd_size_type total;
358
359  bsssize = 0;
360  datasize = 0;
361  textsize = 0;
362
363  bfd_map_over_sections (abfd, berkeley_sum, (PTR) NULL);
364
365  if (files_seen++ == 0)
366#if 0
367    /* Intel doesn't like bss/stk because they don't have core files.  */
368    puts ((radix == octal) ? "text\tdata\tbss/stk\toct\thex\tfilename" :
369	  "text\tdata\tbss/stk\tdec\thex\tfilename");
370#else
371    puts ((radix == octal) ? "text\tdata\tbss\toct\thex\tfilename" :
372	  "text\tdata\tbss\tdec\thex\tfilename");
373#endif
374
375  total = textsize + datasize + bsssize;
376
377  lprint_number (7, textsize);
378  lprint_number (7, datasize);
379  lprint_number (7, bsssize);
380  printf (((radix == octal) ? "%-7lo\t%-7lx\t" : "%-7lu\t%-7lx\t"),
381	  (unsigned long) total, (unsigned long) total);
382
383  fputs (bfd_get_filename (abfd), stdout);
384  if (bfd_my_archive (abfd))
385    printf (" (ex %s)", bfd_get_filename (bfd_my_archive (abfd)));
386}
387
388/* I REALLY miss lexical functions! */
389bfd_size_type svi_total = 0;
390
391static void
392sysv_internal_printer (file, sec, ignore)
393     bfd *file;
394     sec_ptr sec;
395     PTR ignore;
396{
397  bfd_size_type size = bfd_section_size (file, sec);
398  if (!bfd_is_abs_section (sec)
399      && !bfd_is_com_section (sec)
400      && !bfd_is_und_section (sec))
401    {
402      svi_total += size;
403
404      printf ("%-12s", bfd_section_name (file, sec));
405      rprint_number (8, size);
406      printf (" ");
407      rprint_number (8, bfd_section_vma (file, sec));
408      printf ("\n");
409    }
410}
411
412static void
413print_sysv_format (file)
414     bfd *file;
415{
416  svi_total = 0;
417
418  printf ("%s  ", bfd_get_filename (file));
419  if (bfd_my_archive (file))
420    printf (" (ex %s)", bfd_get_filename (bfd_my_archive (file)));
421
422  puts (":\nsection\t\tsize\t     addr");
423  bfd_map_over_sections (file, sysv_internal_printer, (PTR) NULL);
424
425  printf ("Total       ");
426  rprint_number (8, svi_total);
427  printf ("\n\n");
428}
429
430static void
431print_sizes (file)
432     bfd *file;
433{
434  if (berkeley_format)
435    print_berkeley_format (file);
436  else
437    print_sysv_format (file);
438}
439