* Many files: Added gettext invocations around user-visible
[external/binutils.git] / binutils / size.c
1 /* size.c -- report size of various sections of an executable file.
2    Copyright 1991, 92, 93, 94, 95, 96, 97, 1998 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 2 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
19 \f
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
42 enum
43   {
44     decimal, octal, hex
45   } radix = decimal;
46 int berkeley_format = BSD_DEFAULT;      /* 0 means use AT&T-style output.  */
47 int show_version = 0;
48 int show_help = 0;
49
50 /* Program exit status.  */
51 int return_code = 0;
52
53 static char *target = NULL;
54
55 /* Static declarations */
56
57 static void usage PARAMS ((FILE *, int));
58 static void display_file PARAMS ((char *filename));
59 static void display_bfd PARAMS ((bfd *));
60 static void display_archive PARAMS ((bfd *));
61 static int size_number PARAMS ((bfd_size_type));
62 #if 0
63 static void lprint_number PARAMS ((int, bfd_size_type));
64 #endif
65 static void rprint_number PARAMS ((int, bfd_size_type));
66 static void print_berkeley_format PARAMS ((bfd *));
67 static void sysv_internal_sizer PARAMS ((bfd *, asection *, PTR));
68 static void sysv_internal_printer PARAMS ((bfd *, asection *, PTR));
69 static void print_sysv_format PARAMS ((bfd *));
70 static void print_sizes PARAMS ((bfd * file));
71 static void berkeley_sum PARAMS ((bfd *, sec_ptr, PTR));
72 \f
73 static void
74 usage (stream, status)
75      FILE *stream;
76      int status;
77 {
78   fprintf (stream, _("\
79 Usage: %s [-ABdoxV] [--format=berkeley|sysv] [--radix=8|10|16]\n\
80        [--target=bfdname] [--version] [--help] [file...]\n"), program_name);
81 #if BSD_DEFAULT
82   fputs (_("default is --format=berkeley\n"), stream);
83 #else
84   fputs (_("default is --format=sysv\n"), stream);
85 #endif
86   list_supported_targets (program_name, stream);
87   if (status == 0)
88     fprintf (stream, _("Report bugs to bug-gnu-utils@gnu.org\n"));
89   exit (status);
90 }
91
92 struct option long_options[] =
93 {
94   {"format", required_argument, 0, 200},
95   {"radix", required_argument, 0, 201},
96   {"target", required_argument, 0, 202},
97   {"version", no_argument, &show_version, 1},
98   {"help", no_argument, &show_help, 1},
99   {0, no_argument, 0, 0}
100 };
101
102 int
103 main (argc, argv)
104      int argc;
105      char **argv;
106 {
107   int temp;
108   int c;
109
110   program_name = *argv;
111   xmalloc_set_program_name (program_name);
112
113   bfd_init ();
114   set_default_bfd_target ();
115
116   while ((c = getopt_long (argc, argv, "ABVdox", long_options,
117                            (int *) 0)) != EOF)
118     switch (c)
119       {
120       case 200:         /* --format */
121         switch (*optarg)
122           {
123           case 'B':
124           case 'b':
125             berkeley_format = 1;
126             break;
127           case 'S':
128           case 's':
129             berkeley_format = 0;
130             break;
131           default:
132             fprintf (stderr, _("invalid argument to --format: %s\n"), optarg);
133             usage (stderr, 1);
134           }
135         break;
136
137       case 202:         /* --target */
138         target = optarg;
139         break;
140
141       case 201:         /* --radix */
142 #ifdef ANSI_LIBRARIES
143         temp = strtol (optarg, NULL, 10);
144 #else
145         temp = atol (optarg);
146 #endif
147         switch (temp)
148           {
149           case 10:
150             radix = decimal;
151             break;
152           case 8:
153             radix = octal;
154             break;
155           case 16:
156             radix = hex;
157             break;
158           default:
159             printf (_("Invalid radix: %s\n"), optarg);
160             usage (stderr, 1);
161           }
162         break;
163
164       case 'A':
165         berkeley_format = 0;
166         break;
167       case 'B':
168         berkeley_format = 1;
169         break;
170       case 'V':
171         show_version = 1;
172         break;
173       case 'd':
174         radix = decimal;
175         break;
176       case 'x':
177         radix = hex;
178         break;
179       case 'o':
180         radix = octal;
181         break;
182       case 0:
183         break;
184       case '?':
185         usage (stderr, 1);
186       }
187
188   if (show_version)
189     print_version ("size");
190   if (show_help)
191     usage (stdout, 0);
192
193   if (optind == argc)
194     display_file ("a.out");
195   else
196     for (; optind < argc;)
197       display_file (argv[optind++]);
198
199   return return_code;
200 }
201 \f
202 /* Display stats on file or archive member ABFD.  */
203
204 static void
205 display_bfd (abfd)
206      bfd *abfd;
207 {
208   char **matching;
209
210   if (bfd_check_format (abfd, bfd_archive))
211     /* An archive within an archive.  */
212     return;
213
214   if (bfd_check_format_matches (abfd, bfd_object, &matching))
215     {
216       print_sizes (abfd);
217       printf ("\n");
218       return;
219     }
220
221   if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
222     {
223       bfd_nonfatal (bfd_get_filename (abfd));
224       list_matching_formats (matching);
225       free (matching);
226       return_code = 3;
227       return;
228     }
229
230   if (bfd_check_format_matches (abfd, bfd_core, &matching))
231     {
232       CONST char *core_cmd;
233
234       print_sizes (abfd);
235       fputs (" (core file", stdout);
236
237       core_cmd = bfd_core_file_failing_command (abfd);
238       if (core_cmd)
239         printf (" invoked as %s", core_cmd);
240
241       puts (")\n");
242       return;
243     }
244
245   bfd_nonfatal (bfd_get_filename (abfd));
246
247   if (bfd_get_error () == bfd_error_file_ambiguously_recognized)
248     {
249       list_matching_formats (matching);
250       free (matching);
251     }
252
253   return_code = 3;
254 }
255
256 static void
257 display_archive (file)
258      bfd *file;
259 {
260   bfd *arfile = (bfd *) NULL;
261
262   for (;;)
263     {
264       bfd_set_error (bfd_error_no_error);
265
266       arfile = bfd_openr_next_archived_file (file, arfile);
267       if (arfile == NULL)
268         {
269           if (bfd_get_error () != bfd_error_no_more_archived_files)
270             {
271               bfd_nonfatal (bfd_get_filename (file));
272               return_code = 2;
273             }
274           break;
275         }
276
277       display_bfd (arfile);
278       /* Don't close the archive elements; we need them for next_archive */
279     }
280 }
281
282 static void
283 display_file (filename)
284      char *filename;
285 {
286   bfd *file = bfd_openr (filename, target);
287   if (file == NULL)
288     {
289       bfd_nonfatal (filename);
290       return_code = 1;
291       return;
292     }
293
294   if (bfd_check_format (file, bfd_archive) == true)
295     display_archive (file);
296   else
297     display_bfd (file);
298
299   if (bfd_close (file) == false)
300     {
301       bfd_nonfatal (filename);
302       return_code = 1;
303       return;
304     }
305 }
306 \f
307 /* This is what lexical functions are for.  */
308
309 static int
310 size_number (num)
311      bfd_size_type num;
312 {
313   char buffer[40];
314   sprintf (buffer,
315            (radix == decimal ? "%lu" :
316            ((radix == octal) ? "0%lo" : "0x%lx")),
317            (unsigned long) num);
318
319   return strlen (buffer);
320 }
321
322 #if 0
323
324 /* This is not used.  */
325
326 static void
327 lprint_number (width, num)
328      int width;
329      bfd_size_type num;
330 {
331   char buffer[40];
332   sprintf (buffer,
333            (radix == decimal ? "%lu" :
334            ((radix == octal) ? "0%lo" : "0x%lx")),
335            (unsigned long) num);
336
337   printf ("%-*s", width, buffer);
338 }
339
340 #endif
341
342 static void
343 rprint_number (width, num)
344      int width;
345      bfd_size_type num;
346 {
347   char buffer[40];
348   sprintf (buffer,
349            (radix == decimal ? "%lu" :
350            ((radix == octal) ? "0%lo" : "0x%lx")),
351            (unsigned long) num);
352
353   printf ("%*s", width, buffer);
354 }
355
356 static bfd_size_type bsssize;
357 static bfd_size_type datasize;
358 static bfd_size_type textsize;
359
360 static void
361 berkeley_sum (abfd, sec, ignore)
362      bfd *abfd;
363      sec_ptr sec;
364      PTR ignore;
365 {
366   flagword flags;
367   bfd_size_type size;
368
369   flags = bfd_get_section_flags (abfd, sec);
370   if ((flags & SEC_ALLOC) == 0)
371     return;
372
373   size = bfd_get_section_size_before_reloc (sec);
374   if ((flags & SEC_CODE) != 0 || (flags & SEC_READONLY) != 0)
375     textsize += size;
376   else if ((flags & SEC_HAS_CONTENTS) != 0)
377     datasize += size;
378   else
379     bsssize += size;
380 }
381
382 static void 
383 print_berkeley_format (abfd)
384      bfd *abfd;
385 {
386   static int files_seen = 0;
387   bfd_size_type total;
388
389   bsssize = 0;
390   datasize = 0;
391   textsize = 0;
392
393   bfd_map_over_sections (abfd, berkeley_sum, (PTR) NULL);
394
395   if (files_seen++ == 0)
396 #if 0
397     /* Intel doesn't like bss/stk because they don't have core files.  */
398     puts ((radix == octal) ? "   text\t   data\tbss/stk\t    oct\t    hex\tfilename" :
399           "   text\t   data\tbss/stk\t    dec\t    hex\tfilename");
400 #else
401     puts ((radix == octal) ? "   text\t   data\t    bss\t    oct\t    hex\tfilename" :
402           "   text\t   data\t    bss\t    dec\t    hex\tfilename");
403 #endif
404
405   total = textsize + datasize + bsssize;
406
407   rprint_number (7, textsize);
408   putchar ('\t');
409   rprint_number (7, datasize);
410   putchar ('\t');
411   rprint_number (7, bsssize);
412   printf (((radix == octal) ? "\t%7lo\t%7lx\t" : "\t%7lu\t%7lx\t"),
413           (unsigned long) total, (unsigned long) total);
414
415   fputs (bfd_get_filename (abfd), stdout);
416   if (bfd_my_archive (abfd))
417     printf (" (ex %s)", bfd_get_filename (bfd_my_archive (abfd)));
418 }
419
420 /* I REALLY miss lexical functions! */
421 bfd_size_type svi_total = 0;
422 bfd_vma svi_maxvma = 0;
423 int svi_namelen = 0;
424 int svi_vmalen = 0;
425 int svi_sizelen = 0;
426
427 static void
428 sysv_internal_sizer (file, sec, ignore)
429      bfd *file;
430      sec_ptr sec;
431      PTR ignore;
432 {
433   bfd_size_type size = bfd_section_size (file, sec);
434   if (!bfd_is_abs_section (sec)
435       && !bfd_is_com_section (sec)
436       && !bfd_is_und_section (sec))
437     {
438       int namelen = strlen (bfd_section_name (file, sec));
439       if (namelen > svi_namelen)
440         svi_namelen = namelen;
441
442       svi_total += size;
443       if (bfd_section_vma (file, sec) > svi_maxvma)
444         svi_maxvma = bfd_section_vma (file, sec);
445     }
446 }
447
448 static void
449 sysv_internal_printer (file, sec, ignore)
450      bfd *file;
451      sec_ptr sec;
452      PTR ignore;
453 {
454   bfd_size_type size = bfd_section_size (file, sec);
455   if (!bfd_is_abs_section (sec)
456       && !bfd_is_com_section (sec)
457       && !bfd_is_und_section (sec))
458     {
459       svi_total += size;
460
461       printf ("%-*s   ", svi_namelen, bfd_section_name (file, sec));
462       rprint_number (svi_sizelen, size);
463       printf ("   ");
464       rprint_number (svi_vmalen, bfd_section_vma (file, sec));
465       printf ("\n");
466     }
467 }
468
469 static void
470 print_sysv_format (file)
471      bfd *file;
472 {
473   /* size all of the columns */
474   svi_total = 0;
475   svi_maxvma = 0;
476   svi_namelen = 0;
477   bfd_map_over_sections (file, sysv_internal_sizer, (PTR) NULL);
478   svi_vmalen = size_number ((bfd_size_type)svi_maxvma);
479   if ((size_t) svi_vmalen < sizeof ("addr") - 1)
480     svi_vmalen = sizeof ("addr")-1;
481
482   svi_sizelen = size_number (svi_total);
483   if ((size_t) svi_sizelen < sizeof ("size") - 1)
484     svi_sizelen = sizeof ("size")-1;
485
486   svi_total = 0;
487   printf ("%s  ", bfd_get_filename (file));
488   if (bfd_my_archive (file))
489     printf (" (ex %s)", bfd_get_filename (bfd_my_archive (file)));
490
491   printf (":\n%-*s   %*s   %*s\n", svi_namelen, "section",
492           svi_sizelen, "size", svi_vmalen, "addr");
493   bfd_map_over_sections (file, sysv_internal_printer, (PTR) NULL);
494
495   printf ("%-*s   ", svi_namelen, "Total");
496   rprint_number (svi_sizelen, svi_total);
497   printf ("\n\n");
498 }
499
500 static void
501 print_sizes (file)
502      bfd *file;
503 {
504   if (berkeley_format)
505     print_berkeley_format (file);
506   else
507     print_sysv_format (file);
508 }