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