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