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
main(int argc,char ** argv)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
xlatetom(Elf * elf,GElf_Ehdr * elfhdr,void * _src,void * _dst,Elf_Type type,size_t size)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
handle_core_note(Elf * elf,GElf_Ehdr * elfhdr,GElf_Phdr * phdr,char ** cmd_line)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
handle_phdr(Elf * elf,GElf_Ehdr * elfhdr,GElf_Phdr * phdr,uint32_t idx,const char * name)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
handle_core(char const * name,Elf * elf,GElf_Ehdr * elfhdr)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
handle_elf(int fd,const char * name)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
sysv_header(const char * name,Elf_Arhdr * arhdr)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
sysv_calc(Elf * elf,GElf_Ehdr * elfhdr,GElf_Shdr * shdr)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
sysv_footer(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
berkeley_header(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
berkeley_calc(GElf_Shdr * shdr)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
berkeley_totals(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
berkeley_footer(const char * name,const char * ar_name,const char * msg)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
tbl_new(int col)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
tbl_print(const char * s,int col)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
tbl_print_num(uint64_t num,enum radix_style rad,int col)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
tbl_append(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
tbl_flush(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
usage(void)942 usage(void)
943 {
944 (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
945 exit(EXIT_FAILURE);
946 }
947
948 static void
show_version(void)949 show_version(void)
950 {
951 (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
952 exit(EXIT_SUCCESS);
953 }
954