]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/elftoolchain/size/size.c
Copy libevent sources to contrib
[FreeBSD/FreeBSD.git] / contrib / elftoolchain / size / size.c
1 /*-
2  * Copyright (c) 2007 S.Sam Arun Raj
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <assert.h>
28 #include <err.h>
29 #include <fcntl.h>
30 #include <gelf.h>
31 #include <getopt.h>
32 #include <libelftc.h>
33 #include <stdint.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 #include "_elftc.h"
40
41 ELFTC_VCSID("$Id: size.c 3458 2016-05-09 15:01:25Z emaste $");
42
43 #define BUF_SIZE                        1024
44 #define ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1))
45 #define SIZE_VERSION_STRING             "size 1.0"
46
47 enum return_code {
48         RETURN_OK,
49         RETURN_NOINPUT,
50         RETURN_DATAERR,
51         RETURN_USAGE
52 };
53
54 enum output_style {
55         STYLE_BERKELEY,
56         STYLE_SYSV
57 };
58
59 enum radix_style {
60         RADIX_OCTAL,
61         RADIX_DECIMAL,
62         RADIX_HEX
63 };
64
65 static uint64_t bss_size, data_size, text_size, total_size;
66 static uint64_t bss_size_total, data_size_total, text_size_total;
67 static int show_totals;
68 static int size_option;
69 static enum radix_style radix = RADIX_DECIMAL;
70 static enum output_style style = STYLE_BERKELEY;
71 static const char *default_args[2] = { "a.out", NULL };
72
73 static struct {
74         int row;
75         int col;
76         int *width;
77         char ***tbl;
78 } *tb;
79
80 enum {
81         OPT_FORMAT,
82         OPT_RADIX
83 };
84
85 static struct option size_longopts[] = {
86         { "format",     required_argument, &size_option, OPT_FORMAT },
87         { "help",       no_argument,    NULL,   'h' },
88         { "radix",      required_argument, &size_option, OPT_RADIX },
89         { "totals",     no_argument,    NULL,   't' },
90         { "version",    no_argument,    NULL,   'V' },
91         { NULL, 0, NULL, 0 }  
92 };
93
94 static void     berkeley_calc(GElf_Shdr *);
95 static void     berkeley_footer(const char *, const char *, const char *);
96 static void     berkeley_header(void);
97 static void     berkeley_totals(void);
98 static int      handle_core(char const *, Elf *elf, GElf_Ehdr *);
99 static void     handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **);
100 static int      handle_elf(char const *);
101 static void     handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t,
102                     const char *);
103 static void     show_version(void);
104 static void     sysv_header(const char *, Elf_Arhdr *);
105 static void     sysv_footer(void);
106 static void     sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *);
107 static void     usage(void);
108 static void     tbl_new(int);
109 static void     tbl_print(const char *, int);
110 static void     tbl_print_num(uint64_t, enum radix_style, int);
111 static void     tbl_append(void);
112 static void     tbl_flush(void);
113
114 /*
115  * size utility using elf(3) and gelf(3) API to list section sizes and
116  * total in elf files. Supports only elf files (core dumps in elf
117  * included) that can be opened by libelf, other formats are not supported.
118  */
119 int
120 main(int argc, char **argv)
121 {
122         int ch, r, rc;
123         const char **files, *fn;
124
125         rc = RETURN_OK;
126
127         if (elf_version(EV_CURRENT) == EV_NONE)
128                 errx(EXIT_FAILURE, "ELF library initialization failed: %s",
129                     elf_errmsg(-1));
130
131         while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts,
132             NULL)) != -1)
133                 switch((char)ch) {
134                 case 'A':
135                         style = STYLE_SYSV;
136                         break;
137                 case 'B':
138                         style = STYLE_BERKELEY;
139                         break;
140                 case 'V':
141                         show_version();
142                         break;
143                 case 'd':
144                         radix = RADIX_DECIMAL;
145                         break;
146                 case 'o':
147                         radix = RADIX_OCTAL;
148                         break;
149                 case 't':
150                         show_totals = 1;
151                         break;
152                 case 'x':
153                         radix = RADIX_HEX;
154                         break;
155                 case 0:
156                         switch (size_option) {
157                         case OPT_FORMAT:
158                                 if (*optarg == 's' || *optarg == 'S')
159                                         style = STYLE_SYSV;
160                                 else if (*optarg == 'b' || *optarg == 'B')
161                                         style = STYLE_BERKELEY;
162                                 else {
163                                         warnx("unrecognized format \"%s\".",
164                                               optarg);
165                                         usage();
166                                 }
167                                 break;
168                         case OPT_RADIX:
169                                 r = strtol(optarg, NULL, 10);
170                                 if (r == 8)
171                                         radix = RADIX_OCTAL;
172                                 else if (r == 10)
173                                         radix = RADIX_DECIMAL;
174                                 else if (r == 16)
175                                         radix = RADIX_HEX;
176                                 else {
177                                         warnx("unsupported radix \"%s\".",
178                                               optarg);
179                                         usage();
180                                 }
181                                 break;
182                         default:
183                                 err(EXIT_FAILURE, "Error in option handling.");
184                                 /*NOTREACHED*/
185                         }
186                         break;
187                 case 'h':
188                 case '?':
189                 default:
190                         usage();
191                         /* NOTREACHED */
192                 }
193         argc -= optind;
194         argv += optind;
195
196         files = (argc == 0) ? default_args : (void *) argv;
197
198         while ((fn = *files) != NULL) {
199                 rc = handle_elf(fn);
200                 if (rc != RETURN_OK)
201                         warnx(rc == RETURN_NOINPUT ?
202                               "'%s': No such file" :
203                               "%s: File format not recognized", fn);
204                 files++;
205         }
206         if (style == STYLE_BERKELEY) {
207                 if (show_totals)
208                         berkeley_totals();
209                 tbl_flush();
210         }
211         return (rc);
212 }
213
214 static Elf_Data *
215 xlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst,
216     Elf_Type type, size_t size)
217 {
218         Elf_Data src, dst;
219
220         src.d_buf = _src;
221         src.d_type = type;
222         src.d_version = elfhdr->e_version;
223         src.d_size = size;
224         dst.d_buf = _dst;
225         dst.d_version = elfhdr->e_version;
226         dst.d_size = size;
227         return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA]));
228 }
229
230 #define NOTE_OFFSET_32(nhdr, namesz, offset)                    \
231         ((char *)nhdr + sizeof(Elf32_Nhdr) +                    \
232             ELF_ALIGN((int32_t)namesz, 4) + offset)
233
234 #define NOTE_OFFSET_64(nhdr, namesz, offset)                    \
235         ((char *)nhdr + sizeof(Elf32_Nhdr) +                    \
236             ELF_ALIGN((int32_t)namesz, 8) + offset)
237
238 #define PID32(nhdr, namesz, offset)                             \
239         (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr,        \
240             namesz, offset)));
241
242 #define PID64(nhdr, namesz, offset)                             \
243         (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr,        \
244             namesz, offset)));
245
246 #define NEXT_NOTE(elfhdr, descsz, namesz, offset) do {          \
247         if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) {          \
248                 offset += ELF_ALIGN((int32_t)descsz, 4) +       \
249                     sizeof(Elf32_Nhdr) +                        \
250                         ELF_ALIGN((int32_t)namesz, 4);          \
251         } else {                                                \
252                 offset += ELF_ALIGN((int32_t)descsz, 8) +       \
253                     sizeof(Elf32_Nhdr) +                        \
254                         ELF_ALIGN((int32_t)namesz, 8);          \
255         }                                                       \
256 } while (0)
257
258 /*
259  * Parse individual note entries inside a PT_NOTE segment.
260  */
261 static void
262 handle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
263     char **cmd_line)
264 {
265         size_t max_size, segment_end;
266         uint64_t raw_size;
267         GElf_Off offset;
268         static pid_t pid;
269         uintptr_t ver;
270         Elf32_Nhdr *nhdr, nhdr_l;
271         static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/;
272         char buf[BUF_SIZE], *data, *name;
273
274         if (elf == NULL || elfhdr == NULL || phdr == NULL)
275                 return;
276
277         data = elf_rawfile(elf, &max_size);
278         offset = phdr->p_offset;
279         if (offset >= max_size || phdr->p_filesz > max_size - offset) {
280                 warnx("invalid PHDR offset");
281                 return;
282         }
283         segment_end = phdr->p_offset + phdr->p_filesz;
284
285         while (data != NULL && offset + sizeof(Elf32_Nhdr) < segment_end) {
286                 nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset);
287                 memset(&nhdr_l, 0, sizeof(Elf32_Nhdr));
288                 if (!xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type,
289                         ELF_T_WORD, sizeof(Elf32_Word)) ||
290                     !xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz,
291                         ELF_T_WORD, sizeof(Elf32_Word)) ||
292                     !xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz,
293                         ELF_T_WORD, sizeof(Elf32_Word)))
294                         break;
295
296                 if (offset + sizeof(Elf32_Nhdr) +
297                     ELF_ALIGN(nhdr_l.n_namesz, 4) +
298                     ELF_ALIGN(nhdr_l.n_descsz, 4) >= segment_end) {
299                         warnx("invalid note header");
300                         return;
301                 }
302
303                 name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr));
304                 switch (nhdr_l.n_type) {
305                 case NT_PRSTATUS: {
306                         raw_size = 0;
307                         if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD &&
308                             nhdr_l.n_namesz == 0x8 &&
309                             !strcmp(name,"FreeBSD")) {
310                                 if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) {
311                                         raw_size = (uint64_t)*((uint32_t *)
312                                             (uintptr_t)(name +
313                                                 ELF_ALIGN((int32_t)
314                                                 nhdr_l.n_namesz, 4) + 8));
315                                         ver = (uintptr_t)NOTE_OFFSET_32(nhdr,
316                                             nhdr_l.n_namesz,0);
317                                         if (*((int *)ver) == 1)
318                                                 pid = PID32(nhdr,
319                                                     nhdr_l.n_namesz, 24);
320                                 } else {
321                                         raw_size = *((uint64_t *)(uintptr_t)
322                                             (name + ELF_ALIGN((int32_t)
323                                                 nhdr_l.n_namesz, 8) + 16));
324                                         ver = (uintptr_t)NOTE_OFFSET_64(nhdr,
325                                             nhdr_l.n_namesz,0);
326                                         if (*((int *)ver) == 1)
327                                                 pid = PID64(nhdr,
328                                                     nhdr_l.n_namesz, 40);
329                                 }
330                                 xlatetom(elf, elfhdr, &raw_size, &raw_size,
331                                     ELF_T_WORD, sizeof(uint64_t));
332                                 xlatetom(elf, elfhdr, &pid, &pid, ELF_T_WORD,
333                                     sizeof(pid_t));
334                         }
335
336                         if (raw_size != 0 && style == STYLE_SYSV) {
337                                 (void) snprintf(buf, BUF_SIZE, "%s/%d",
338                                     ".reg", pid);
339                                 tbl_append();
340                                 tbl_print(buf, 0);
341                                 tbl_print_num(raw_size, radix, 1);
342                                 tbl_print_num(0, radix, 2);
343                                 if (!reg_pseudo) {
344                                         tbl_append();
345                                         tbl_print(".reg", 0);
346                                         tbl_print_num(raw_size, radix, 1);
347                                         tbl_print_num(0, radix, 2);
348                                         reg_pseudo = 1;
349                                         text_size_total += raw_size;
350                                 }
351                                 text_size_total += raw_size;
352                         }
353                 }
354                 break;
355                 case NT_FPREGSET:       /* same as NT_PRFPREG */
356                         if (style == STYLE_SYSV) {
357                                 (void) snprintf(buf, BUF_SIZE,
358                                     "%s/%d", ".reg2", pid);
359                                 tbl_append();
360                                 tbl_print(buf, 0);
361                                 tbl_print_num(nhdr_l.n_descsz, radix, 1);
362                                 tbl_print_num(0, radix, 2);
363                                 if (!reg2_pseudo) {
364                                         tbl_append();
365                                         tbl_print(".reg2", 0);
366                                         tbl_print_num(nhdr_l.n_descsz, radix,
367                                             1);
368                                         tbl_print_num(0, radix, 2);
369                                         reg2_pseudo = 1;
370                                         text_size_total += nhdr_l.n_descsz;
371                                 }
372                                 text_size_total += nhdr_l.n_descsz;
373                         }
374                         break;
375 #if 0
376                 case NT_AUXV:
377                         if (style == STYLE_SYSV) {
378                                 tbl_append();
379                                 tbl_print(".auxv", 0);
380                                 tbl_print_num(nhdr_l.n_descsz, radix, 1);
381                                 tbl_print_num(0, radix, 2);
382                                 text_size_total += nhdr_l.n_descsz;
383                         }
384                         break;
385                 case NT_PRXFPREG:
386                         if (style == STYLE_SYSV) {
387                                 (void) snprintf(buf, BUF_SIZE, "%s/%d",
388                                     ".reg-xfp", pid);
389                                 tbl_append();
390                                 tbl_print(buf, 0);
391                                 tbl_print_num(nhdr_l.n_descsz, radix, 1);
392                                 tbl_print_num(0, radix, 2);
393                                 if (!regxfp_pseudo) {
394                                         tbl_append();
395                                         tbl_print(".reg-xfp", 0);
396                                         tbl_print_num(nhdr_l.n_descsz, radix,
397                                             1);
398                                         tbl_print_num(0, radix, 2);
399                                         regxfp_pseudo = 1;
400                                         text_size_total += nhdr_l.n_descsz;
401                                 }
402                                 text_size_total += nhdr_l.n_descsz;
403                         }
404                         break;
405                 case NT_PSINFO:
406 #endif
407                 case NT_PRPSINFO: {
408                         /* FreeBSD 64-bit */
409                         if (nhdr_l.n_descsz == 0x78 &&
410                                 !strcmp(name,"FreeBSD")) {
411                                 *cmd_line = strdup(NOTE_OFFSET_64(nhdr,
412                                     nhdr_l.n_namesz, 33));
413                         /* FreeBSD 32-bit */
414                         } else if (nhdr_l.n_descsz == 0x6c &&
415                                 !strcmp(name,"FreeBSD")) {
416                                 *cmd_line = strdup(NOTE_OFFSET_32(nhdr,
417                                     nhdr_l.n_namesz, 25));
418                         }
419                         /* Strip any trailing spaces */
420                         if (*cmd_line != NULL) {
421                                 char *s;
422
423                                 s = *cmd_line + strlen(*cmd_line);
424                                 while (s > *cmd_line) {
425                                         if (*(s-1) != 0x20) break;
426                                         s--;
427                                 }
428                                 *s = 0;
429                         }
430                         break;
431                 }
432 #if 0
433                 case NT_PSTATUS:
434                 case NT_LWPSTATUS:
435 #endif
436                 default:
437                         break;
438                 }
439                 NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset);
440         }
441 }
442
443 /*
444  * Handles program headers except for PT_NOTE, when sysv output style is
445  * chosen, prints out the segment name and length. For berkely output
446  * style only PT_LOAD segments are handled, and text,
447  * data, bss size is calculated for them.
448  */
449 static void
450 handle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
451     uint32_t idx, const char *name)
452 {
453         uint64_t addr, size;
454         int split;
455         char buf[BUF_SIZE];
456
457         if (elf == NULL || elfhdr == NULL || phdr == NULL)
458                 return;
459
460         split = (phdr->p_memsz > 0) &&  (phdr->p_filesz > 0) &&
461             (phdr->p_memsz > phdr->p_filesz);
462
463         if (style == STYLE_SYSV) {
464                 (void) snprintf(buf, BUF_SIZE,
465                     "%s%d%s", name, idx, (split ? "a" : ""));
466                 tbl_append();
467                 tbl_print(buf, 0);
468                 tbl_print_num(phdr->p_filesz, radix, 1);
469                 tbl_print_num(phdr->p_vaddr, radix, 2);
470                 text_size_total += phdr->p_filesz;
471                 if (split) {
472                         size = phdr->p_memsz - phdr->p_filesz;
473                         addr = phdr->p_vaddr + phdr->p_filesz;
474                         (void) snprintf(buf, BUF_SIZE, "%s%d%s", name,
475                             idx, "b");
476                         text_size_total += phdr->p_memsz - phdr->p_filesz;
477                         tbl_append();
478                         tbl_print(buf, 0);
479                         tbl_print_num(size, radix, 1);
480                         tbl_print_num(addr, radix, 2);
481                 }
482         } else {
483                 if (phdr->p_type != PT_LOAD)
484                         return;
485                 if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) {
486                         data_size += phdr->p_filesz;
487                         if (split)
488                                 data_size += phdr->p_memsz - phdr->p_filesz;
489                 } else {
490                         text_size += phdr->p_filesz;
491                         if (split)
492                                 text_size += phdr->p_memsz - phdr->p_filesz;
493                 }
494         }
495 }
496
497 /*
498  * Given a core dump file, this function maps program headers to segments.
499  */
500 static int
501 handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr)
502 {
503         GElf_Phdr phdr;
504         uint32_t i;
505         char *core_cmdline;
506         const char *seg_name;
507
508         if (name == NULL || elf == NULL || elfhdr == NULL)
509                 return (RETURN_DATAERR);
510         if  (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE)
511                 return (RETURN_DATAERR);
512
513         seg_name = core_cmdline = NULL;
514         if (style == STYLE_SYSV)
515                 sysv_header(name, NULL);
516         else
517                 berkeley_header();
518
519         for (i = 0; i < elfhdr->e_phnum; i++) {
520                 if (gelf_getphdr(elf, i, &phdr) != NULL) {
521                         if (phdr.p_type == PT_NOTE) {
522                                 handle_phdr(elf, elfhdr, &phdr, i, "note");
523                                 handle_core_note(elf, elfhdr, &phdr,
524                                     &core_cmdline);
525                         } else {
526                                 switch(phdr.p_type) {
527                                 case PT_NULL:
528                                         seg_name = "null";
529                                         break;
530                                 case PT_LOAD:
531                                         seg_name = "load";
532                                         break;
533                                 case PT_DYNAMIC:
534                                         seg_name = "dynamic";
535                                         break;
536                                 case PT_INTERP:
537                                         seg_name = "interp";
538                                         break;
539                                 case PT_SHLIB:
540                                         seg_name = "shlib";
541                                         break;
542                                 case PT_PHDR:
543                                         seg_name = "phdr";
544                                         break;
545                                 case PT_GNU_EH_FRAME:
546                                         seg_name = "eh_frame_hdr";
547                                         break;
548                                 case PT_GNU_STACK:
549                                         seg_name = "stack";
550                                         break;
551                                 default:
552                                         seg_name = "segment";
553                                 }
554                                 handle_phdr(elf, elfhdr, &phdr, i, seg_name);
555                         }
556                 }
557         }
558
559         if (style == STYLE_BERKELEY) {
560                 if (core_cmdline != NULL) {
561                         berkeley_footer(core_cmdline, name,
562                             "core file invoked as");
563                 } else {
564                         berkeley_footer(core_cmdline, name, "core file");
565                 }
566         } else {
567                 sysv_footer();
568                 if (core_cmdline != NULL) {
569                         (void) printf(" (core file invoked as %s)\n\n",
570                             core_cmdline);
571                 } else {
572                         (void) printf(" (core file)\n\n");
573                 }
574         }
575         free(core_cmdline);
576         return (RETURN_OK);
577 }
578
579 /*
580  * Given an elf object,ar(1) filename, and based on the output style
581  * and radix format the various sections and their length will be printed
582  * or the size of the text, data, bss sections will be printed out.
583  */
584 static int
585 handle_elf(char const *name)
586 {
587         GElf_Ehdr elfhdr;
588         GElf_Shdr shdr;
589         Elf *elf, *elf1;
590         Elf_Arhdr *arhdr;
591         Elf_Scn *scn;
592         Elf_Cmd elf_cmd;
593         int exit_code, fd;
594
595         if (name == NULL)
596                 return (RETURN_NOINPUT);
597
598         if ((fd = open(name, O_RDONLY, 0)) < 0)
599                 return (RETURN_NOINPUT);
600
601         elf_cmd = ELF_C_READ;
602         elf1 = elf_begin(fd, elf_cmd, NULL);
603         while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) {
604                 arhdr = elf_getarhdr(elf);
605                 if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) {
606                         (void) elf_end(elf);
607                         (void) elf_end(elf1);
608                         (void) close(fd);
609                         return (RETURN_DATAERR);
610                 }
611                 if (elf_kind(elf) != ELF_K_ELF ||
612                     (gelf_getehdr(elf, &elfhdr) == NULL)) {
613                         elf_cmd = elf_next(elf);
614                         (void) elf_end(elf);
615                         warnx("%s: File format not recognized",
616                             arhdr != NULL ? arhdr->ar_name : name);
617                         continue;
618                 }
619                 /* Core dumps are handled separately */
620                 if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) {
621                         exit_code = handle_core(name, elf, &elfhdr);
622                         (void) elf_end(elf);
623                         (void) elf_end(elf1);
624                         (void) close(fd);
625                         return (exit_code);
626                 } else {
627                         scn = NULL;
628                         if (style == STYLE_BERKELEY) {
629                                 berkeley_header();
630                                 while ((scn = elf_nextscn(elf, scn)) != NULL) {
631                                         if (gelf_getshdr(scn, &shdr) != NULL)
632                                                 berkeley_calc(&shdr);
633                                 }
634                         } else {
635                                 sysv_header(name, arhdr);
636                                 scn = NULL;
637                                 while ((scn = elf_nextscn(elf, scn)) != NULL) {
638                                         if (gelf_getshdr(scn, &shdr) != NULL)
639                                                 sysv_calc(elf, &elfhdr, &shdr);
640                                 }
641                         }
642                         if (style == STYLE_BERKELEY) {
643                                 if (arhdr != NULL) {
644                                         berkeley_footer(name, arhdr->ar_name,
645                                             "ex");
646                                 } else {
647                                         berkeley_footer(name, NULL, "ex");
648                                 }
649                         } else {
650                                 sysv_footer();
651                         }
652                 }
653                 elf_cmd = elf_next(elf);
654                 (void) elf_end(elf);
655         }
656         (void) elf_end(elf1);
657         (void) close(fd);
658         return (RETURN_OK);
659 }
660
661 /*
662  * Sysv formatting helper functions.
663  */
664 static void
665 sysv_header(const char *name, Elf_Arhdr *arhdr)
666 {
667
668         text_size_total = 0;
669         if (arhdr != NULL)
670                 (void) printf("%s   (ex %s):\n", arhdr->ar_name, name);
671         else
672                 (void) printf("%s  :\n", name);
673         tbl_new(3);
674         tbl_append();
675         tbl_print("section", 0);
676         tbl_print("size", 1);
677         tbl_print("addr", 2);
678 }
679
680 static void
681 sysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr)
682 {
683         char *section_name;
684
685         section_name = elf_strptr(elf, elfhdr->e_shstrndx,
686             (size_t) shdr->sh_name);
687         if ((shdr->sh_type == SHT_SYMTAB ||
688             shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA ||
689             shdr->sh_type == SHT_REL) && shdr->sh_addr == 0)
690                 return;
691         tbl_append();
692         tbl_print(section_name, 0);
693         tbl_print_num(shdr->sh_size, radix, 1);
694         tbl_print_num(shdr->sh_addr, radix, 2);
695         text_size_total += shdr->sh_size;
696 }
697
698 static void
699 sysv_footer(void)
700 {
701         tbl_append();
702         tbl_print("Total", 0);
703         tbl_print_num(text_size_total, radix, 1);
704         tbl_flush();
705         putchar('\n');
706 }
707
708 /*
709  * berkeley style output formatting helper functions.
710  */
711 static void
712 berkeley_header(void)
713 {
714         static int printed;
715
716         text_size = data_size = bss_size = 0;
717         if (!printed) {
718                 tbl_new(6);
719                 tbl_append();
720                 tbl_print("text", 0);
721                 tbl_print("data", 1);
722                 tbl_print("bss", 2);
723                 if (radix == RADIX_OCTAL)
724                         tbl_print("oct", 3);
725                 else
726                         tbl_print("dec", 3);
727                 tbl_print("hex", 4);
728                 tbl_print("filename", 5);
729                 printed = 1;
730         }
731 }
732
733 static void
734 berkeley_calc(GElf_Shdr *shdr)
735 {
736         if (shdr != NULL) {
737                 if (!(shdr->sh_flags & SHF_ALLOC))
738                         return;
739                 if ((shdr->sh_flags & SHF_ALLOC) &&
740                     ((shdr->sh_flags & SHF_EXECINSTR) ||
741                     !(shdr->sh_flags & SHF_WRITE)))
742                         text_size += shdr->sh_size;
743                 else if ((shdr->sh_flags & SHF_ALLOC) &&
744                     (shdr->sh_flags & SHF_WRITE) &&
745                     (shdr->sh_type != SHT_NOBITS))
746                         data_size += shdr->sh_size;
747                 else
748                         bss_size += shdr->sh_size;
749         }
750 }
751
752 static void
753 berkeley_totals(void)
754 {
755         uint64_t grand_total;
756
757         grand_total = text_size_total + data_size_total + bss_size_total;
758         tbl_append();
759         tbl_print_num(text_size_total, radix, 0);
760         tbl_print_num(data_size_total, radix, 1);
761         tbl_print_num(bss_size_total, radix, 2);
762         if (radix == RADIX_OCTAL)
763                 tbl_print_num(grand_total, RADIX_OCTAL, 3);
764         else
765                 tbl_print_num(grand_total, RADIX_DECIMAL, 3);
766         tbl_print_num(grand_total, RADIX_HEX, 4);
767 }
768
769 static void
770 berkeley_footer(const char *name, const char *ar_name, const char *msg)
771 {
772         char buf[BUF_SIZE];
773
774         total_size = text_size + data_size + bss_size;
775         if (show_totals) {
776                 text_size_total += text_size;
777                 bss_size_total += bss_size;
778                 data_size_total += data_size;
779         }
780
781         tbl_append();
782         tbl_print_num(text_size, radix, 0);
783         tbl_print_num(data_size, radix, 1);
784         tbl_print_num(bss_size, radix, 2);
785         if (radix == RADIX_OCTAL)
786                 tbl_print_num(total_size, RADIX_OCTAL, 3);
787         else
788                 tbl_print_num(total_size, RADIX_DECIMAL, 3);
789         tbl_print_num(total_size, RADIX_HEX, 4);
790         if (ar_name != NULL && name != NULL)
791                 (void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg,
792                     name);
793         else if (ar_name != NULL && name == NULL)
794                 (void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg);
795         else
796                 (void) snprintf(buf, BUF_SIZE, "%s", name);
797         tbl_print(buf, 5);
798 }
799
800
801 static void
802 tbl_new(int col)
803 {
804
805         assert(tb == NULL);
806         assert(col > 0);
807         if ((tb = calloc(1, sizeof(*tb))) == NULL)
808                 err(EXIT_FAILURE, "calloc");
809         if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL)
810                 err(EXIT_FAILURE, "calloc");
811         if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL)
812                 err(EXIT_FAILURE, "calloc");
813         tb->col = col;
814         tb->row = 0;
815 }
816
817 static void
818 tbl_print(const char *s, int col)
819 {
820         int len;
821
822         assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col);
823         assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL);
824         if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL)
825                 err(EXIT_FAILURE, "strdup");
826         len = strlen(s);
827         if (len > tb->width[col])
828                 tb->width[col] = len;
829 }
830
831 static void
832 tbl_print_num(uint64_t num, enum radix_style rad, int col)
833 {
834         char buf[BUF_SIZE];
835
836         (void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" :
837             ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num);
838         tbl_print(buf, col);
839 }
840
841 static void
842 tbl_append(void)
843 {
844         int i;
845
846         assert(tb != NULL && tb->col > 0);
847         tb->row++;
848         for (i = 0; i < tb->col; i++) {
849                 tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row);
850                 if (tb->tbl[i] == NULL)
851                         err(EXIT_FAILURE, "realloc");
852                 tb->tbl[i][tb->row - 1] = NULL;
853         }
854 }
855
856 static void
857 tbl_flush(void)
858 {
859         const char *str;
860         int i, j;
861
862         if (tb == NULL)
863                 return;
864
865         assert(tb->col > 0);
866         for (i = 0; i < tb->row; i++) {
867                 if (style == STYLE_BERKELEY)
868                         printf("  ");
869                 for (j = 0; j < tb->col; j++) {
870                         str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : "");
871                         if (style == STYLE_SYSV && j == 0)
872                                 printf("%-*s", tb->width[j], str);
873                         else if (style == STYLE_BERKELEY && j == tb->col - 1)
874                                 printf("%s", str);
875                         else
876                                 printf("%*s", tb->width[j], str);
877                         if (j == tb->col -1)
878                                 putchar('\n');
879                         else
880                                 printf("   ");
881                 }
882         }
883
884         for (i = 0; i < tb->col; i++) {
885                 for (j = 0; j < tb->row; j++) {
886                         if (tb->tbl[i][j])
887                                 free(tb->tbl[i][j]);
888                 }
889                 free(tb->tbl[i]);
890         }
891         free(tb->tbl);
892         free(tb->width);
893         free(tb);
894         tb = NULL;
895 }
896
897 #define USAGE_MESSAGE   "\
898 Usage: %s [options] file ...\n\
899   Display sizes of ELF sections.\n\n\
900   Options:\n\
901   --format=format    Display output in specified format.  Supported\n\
902                      values are `berkeley' and `sysv'.\n\
903   --help             Display this help message and exit.\n\
904   --radix=radix      Display numeric values in the specified radix.\n\
905                      Supported values are: 8, 10 and 16.\n\
906   --totals           Show cumulative totals of section sizes.\n\
907   --version          Display a version identifier and exit.\n\
908   -A                 Equivalent to `--format=sysv'.\n\
909   -B                 Equivalent to `--format=berkeley'.\n\
910   -V                 Equivalent to `--version'.\n\
911   -d                 Equivalent to `--radix=10'.\n\
912   -h                 Same as option --help.\n\
913   -o                 Equivalent to `--radix=8'.\n\
914   -t                 Equivalent to option --totals.\n\
915   -x                 Equivalent to `--radix=16'.\n"
916
917 static void
918 usage(void)
919 {
920         (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
921         exit(EXIT_FAILURE);
922 }
923
924 static void
925 show_version(void)
926 {
927         (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
928         exit(EXIT_SUCCESS);
929 }