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