2 * Copyright (c) 2007 S.Sam Arun Raj
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
28 #include <capsicum_helpers.h>
40 #include <libcasper.h>
41 #include <casper/cap_fileargs.h>
45 ELFTC_VCSID("$Id: size.c 3458 2016-05-09 15:01:25Z emaste $");
48 #define ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1))
49 #define SIZE_VERSION_STRING "size 1.0"
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;
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' },
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,
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);
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.
122 main(int argc, char **argv)
132 if (elf_version(EV_CURRENT) == EV_NONE)
133 errx(EXIT_FAILURE, "ELF library initialization failed: %s",
136 while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts,
143 style = STYLE_BERKELEY;
149 radix = RADIX_DECIMAL;
161 switch (size_option) {
163 if (*optarg == 's' || *optarg == 'S')
165 else if (*optarg == 'b' || *optarg == 'B')
166 style = STYLE_BERKELEY;
168 warnx("unrecognized format \"%s\".",
174 r = strtol(optarg, NULL, 10);
178 radix = RADIX_DECIMAL;
182 warnx("unsupported radix \"%s\".",
188 err(EXIT_FAILURE, "Error in option handling.");
202 defaultfn = strdup("a.out");
203 if (defaultfn == NULL)
204 err(EXIT_FAILURE, "strdup");
211 cap_rights_init(&rights, CAP_FSTAT, CAP_MMAP_R);
212 fa = fileargs_init(argc, argv, O_RDONLY, 0, &rights, FA_OPEN);
214 err(EXIT_FAILURE, "failed to initialize fileargs");
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");
222 for (; argc > 0; argc--, argv++) {
224 fd = fileargs_open(fa, fn);
226 warn("%s: Failed to open", fn);
229 rc = handle_elf(fd, fn);
231 warnx("%s: File format not recognized", fn);
233 if (style == STYLE_BERKELEY) {
244 xlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst,
245 Elf_Type type, size_t size)
251 src.d_version = elfhdr->e_version;
254 dst.d_version = elfhdr->e_version;
256 return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA]));
259 #define NOTE_OFFSET_32(nhdr, namesz, offset) \
260 ((char *)nhdr + sizeof(Elf32_Nhdr) + \
261 ELF_ALIGN((int32_t)namesz, 4) + offset)
263 #define NOTE_OFFSET_64(nhdr, namesz, offset) \
264 ((char *)nhdr + sizeof(Elf32_Nhdr) + \
265 ELF_ALIGN((int32_t)namesz, 8) + offset)
267 #define PID32(nhdr, namesz, offset) \
268 (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr, \
271 #define PID64(nhdr, namesz, offset) \
272 (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr, \
275 #define NEXT_NOTE(elfhdr, descsz, namesz, offset) do { \
276 if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { \
277 offset += ELF_ALIGN((int32_t)descsz, 4) + \
278 sizeof(Elf32_Nhdr) + \
279 ELF_ALIGN((int32_t)namesz, 4); \
281 offset += ELF_ALIGN((int32_t)descsz, 8) + \
282 sizeof(Elf32_Nhdr) + \
283 ELF_ALIGN((int32_t)namesz, 8); \
288 * Parse individual note entries inside a PT_NOTE segment.
291 handle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
294 size_t max_size, segment_end;
299 Elf32_Nhdr *nhdr, nhdr_l;
300 static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/;
301 char buf[BUF_SIZE], *data, *name;
303 if (elf == NULL || elfhdr == NULL || phdr == NULL)
306 data = elf_rawfile(elf, &max_size);
307 offset = phdr->p_offset;
308 if (offset >= max_size || phdr->p_filesz > max_size - offset) {
309 warnx("invalid PHDR offset");
312 segment_end = phdr->p_offset + phdr->p_filesz;
314 while (data != NULL && offset + sizeof(Elf32_Nhdr) < segment_end) {
315 nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset);
316 memset(&nhdr_l, 0, sizeof(Elf32_Nhdr));
317 if (!xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type,
318 ELF_T_WORD, sizeof(Elf32_Word)) ||
319 !xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz,
320 ELF_T_WORD, sizeof(Elf32_Word)) ||
321 !xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz,
322 ELF_T_WORD, sizeof(Elf32_Word)))
325 if (offset + sizeof(Elf32_Nhdr) +
326 ELF_ALIGN(nhdr_l.n_namesz, 4) +
327 ELF_ALIGN(nhdr_l.n_descsz, 4) >= segment_end) {
328 warnx("invalid note header");
332 name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr));
333 switch (nhdr_l.n_type) {
336 if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD &&
337 nhdr_l.n_namesz == 0x8 &&
338 !strcmp(name,"FreeBSD")) {
339 if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) {
340 raw_size = (uint64_t)*((uint32_t *)
343 nhdr_l.n_namesz, 4) + 8));
344 ver = (uintptr_t)NOTE_OFFSET_32(nhdr,
346 if (*((int *)ver) == 1)
348 nhdr_l.n_namesz, 24);
350 raw_size = *((uint64_t *)(uintptr_t)
351 (name + ELF_ALIGN((int32_t)
352 nhdr_l.n_namesz, 8) + 16));
353 ver = (uintptr_t)NOTE_OFFSET_64(nhdr,
355 if (*((int *)ver) == 1)
357 nhdr_l.n_namesz, 40);
359 xlatetom(elf, elfhdr, &raw_size, &raw_size,
360 ELF_T_WORD, sizeof(uint64_t));
361 xlatetom(elf, elfhdr, &pid, &pid, ELF_T_WORD,
365 if (raw_size != 0 && style == STYLE_SYSV) {
366 (void) snprintf(buf, BUF_SIZE, "%s/%d",
370 tbl_print_num(raw_size, radix, 1);
371 tbl_print_num(0, radix, 2);
374 tbl_print(".reg", 0);
375 tbl_print_num(raw_size, radix, 1);
376 tbl_print_num(0, radix, 2);
378 text_size_total += raw_size;
380 text_size_total += raw_size;
384 case NT_FPREGSET: /* same as NT_PRFPREG */
385 if (style == STYLE_SYSV) {
386 (void) snprintf(buf, BUF_SIZE,
387 "%s/%d", ".reg2", pid);
390 tbl_print_num(nhdr_l.n_descsz, radix, 1);
391 tbl_print_num(0, radix, 2);
394 tbl_print(".reg2", 0);
395 tbl_print_num(nhdr_l.n_descsz, radix,
397 tbl_print_num(0, radix, 2);
399 text_size_total += nhdr_l.n_descsz;
401 text_size_total += nhdr_l.n_descsz;
406 if (style == STYLE_SYSV) {
408 tbl_print(".auxv", 0);
409 tbl_print_num(nhdr_l.n_descsz, radix, 1);
410 tbl_print_num(0, radix, 2);
411 text_size_total += nhdr_l.n_descsz;
415 if (style == STYLE_SYSV) {
416 (void) snprintf(buf, BUF_SIZE, "%s/%d",
420 tbl_print_num(nhdr_l.n_descsz, radix, 1);
421 tbl_print_num(0, radix, 2);
422 if (!regxfp_pseudo) {
424 tbl_print(".reg-xfp", 0);
425 tbl_print_num(nhdr_l.n_descsz, radix,
427 tbl_print_num(0, radix, 2);
429 text_size_total += nhdr_l.n_descsz;
431 text_size_total += nhdr_l.n_descsz;
438 if (nhdr_l.n_descsz == 0x78 &&
439 !strcmp(name,"FreeBSD")) {
440 *cmd_line = strdup(NOTE_OFFSET_64(nhdr,
441 nhdr_l.n_namesz, 33));
443 } else if (nhdr_l.n_descsz == 0x6c &&
444 !strcmp(name,"FreeBSD")) {
445 *cmd_line = strdup(NOTE_OFFSET_32(nhdr,
446 nhdr_l.n_namesz, 25));
448 /* Strip any trailing spaces */
449 if (*cmd_line != NULL) {
452 s = *cmd_line + strlen(*cmd_line);
453 while (s > *cmd_line) {
454 if (*(s-1) != 0x20) break;
468 NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset);
473 * Handles program headers except for PT_NOTE, when sysv output style is
474 * chosen, prints out the segment name and length. For berkely output
475 * style only PT_LOAD segments are handled, and text,
476 * data, bss size is calculated for them.
479 handle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
480 uint32_t idx, const char *name)
486 if (elf == NULL || elfhdr == NULL || phdr == NULL)
489 split = (phdr->p_memsz > 0) && (phdr->p_filesz > 0) &&
490 (phdr->p_memsz > phdr->p_filesz);
492 if (style == STYLE_SYSV) {
493 (void) snprintf(buf, BUF_SIZE,
494 "%s%d%s", name, idx, (split ? "a" : ""));
497 tbl_print_num(phdr->p_filesz, radix, 1);
498 tbl_print_num(phdr->p_vaddr, radix, 2);
499 text_size_total += phdr->p_filesz;
501 size = phdr->p_memsz - phdr->p_filesz;
502 addr = phdr->p_vaddr + phdr->p_filesz;
503 (void) snprintf(buf, BUF_SIZE, "%s%d%s", name,
505 text_size_total += phdr->p_memsz - phdr->p_filesz;
508 tbl_print_num(size, radix, 1);
509 tbl_print_num(addr, radix, 2);
512 if (phdr->p_type != PT_LOAD)
514 if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) {
515 data_size += phdr->p_filesz;
517 data_size += phdr->p_memsz - phdr->p_filesz;
519 text_size += phdr->p_filesz;
521 text_size += phdr->p_memsz - phdr->p_filesz;
527 * Given a core dump file, this function maps program headers to segments.
530 handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr)
535 const char *seg_name;
537 if (name == NULL || elf == NULL || elfhdr == NULL)
538 return (RETURN_DATAERR);
539 if (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE)
540 return (RETURN_DATAERR);
542 seg_name = core_cmdline = NULL;
543 if (style == STYLE_SYSV)
544 sysv_header(name, NULL);
548 for (i = 0; i < elfhdr->e_phnum; i++) {
549 if (gelf_getphdr(elf, i, &phdr) != NULL) {
550 if (phdr.p_type == PT_NOTE) {
551 handle_phdr(elf, elfhdr, &phdr, i, "note");
552 handle_core_note(elf, elfhdr, &phdr,
555 switch(phdr.p_type) {
563 seg_name = "dynamic";
574 case PT_GNU_EH_FRAME:
575 seg_name = "eh_frame_hdr";
581 seg_name = "segment";
583 handle_phdr(elf, elfhdr, &phdr, i, seg_name);
588 if (style == STYLE_BERKELEY) {
589 if (core_cmdline != NULL) {
590 berkeley_footer(core_cmdline, name,
591 "core file invoked as");
593 berkeley_footer(core_cmdline, name, "core file");
597 if (core_cmdline != NULL) {
598 (void) printf(" (core file invoked as %s)\n\n",
601 (void) printf(" (core file)\n\n");
609 * Given an elf object,ar(1) filename, and based on the output style
610 * and radix format the various sections and their length will be printed
611 * or the size of the text, data, bss sections will be printed out.
614 handle_elf(int fd, const char *name)
624 elf_cmd = ELF_C_READ;
625 elf1 = elf_begin(fd, elf_cmd, NULL);
626 while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) {
627 arhdr = elf_getarhdr(elf);
628 if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) {
630 (void) elf_end(elf1);
632 return (RETURN_DATAERR);
634 if (elf_kind(elf) != ELF_K_ELF ||
635 (gelf_getehdr(elf, &elfhdr) == NULL)) {
636 elf_cmd = elf_next(elf);
638 warnx("%s: File format not recognized",
639 arhdr != NULL ? arhdr->ar_name : name);
642 /* Core dumps are handled separately */
643 if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) {
644 exit_code = handle_core(name, elf, &elfhdr);
646 (void) elf_end(elf1);
651 if (style == STYLE_BERKELEY) {
653 while ((scn = elf_nextscn(elf, scn)) != NULL) {
654 if (gelf_getshdr(scn, &shdr) != NULL)
655 berkeley_calc(&shdr);
658 sysv_header(name, arhdr);
660 while ((scn = elf_nextscn(elf, scn)) != NULL) {
661 if (gelf_getshdr(scn, &shdr) != NULL)
662 sysv_calc(elf, &elfhdr, &shdr);
665 if (style == STYLE_BERKELEY) {
667 berkeley_footer(name, arhdr->ar_name,
670 berkeley_footer(name, NULL, "ex");
676 elf_cmd = elf_next(elf);
679 (void) elf_end(elf1);
685 * Sysv formatting helper functions.
688 sysv_header(const char *name, Elf_Arhdr *arhdr)
693 (void) printf("%s (ex %s):\n", arhdr->ar_name, name);
695 (void) printf("%s :\n", name);
698 tbl_print("section", 0);
699 tbl_print("size", 1);
700 tbl_print("addr", 2);
704 sysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr)
708 section_name = elf_strptr(elf, elfhdr->e_shstrndx,
709 (size_t) shdr->sh_name);
710 if ((shdr->sh_type == SHT_SYMTAB ||
711 shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA ||
712 shdr->sh_type == SHT_REL) && shdr->sh_addr == 0)
715 tbl_print(section_name, 0);
716 tbl_print_num(shdr->sh_size, radix, 1);
717 tbl_print_num(shdr->sh_addr, radix, 2);
718 text_size_total += shdr->sh_size;
725 tbl_print("Total", 0);
726 tbl_print_num(text_size_total, radix, 1);
732 * berkeley style output formatting helper functions.
735 berkeley_header(void)
739 text_size = data_size = bss_size = 0;
743 tbl_print("text", 0);
744 tbl_print("data", 1);
746 if (radix == RADIX_OCTAL)
751 tbl_print("filename", 5);
757 berkeley_calc(GElf_Shdr *shdr)
760 if (!(shdr->sh_flags & SHF_ALLOC))
762 if ((shdr->sh_flags & SHF_ALLOC) &&
763 ((shdr->sh_flags & SHF_EXECINSTR) ||
764 !(shdr->sh_flags & SHF_WRITE)))
765 text_size += shdr->sh_size;
766 else if ((shdr->sh_flags & SHF_ALLOC) &&
767 (shdr->sh_flags & SHF_WRITE) &&
768 (shdr->sh_type != SHT_NOBITS))
769 data_size += shdr->sh_size;
771 bss_size += shdr->sh_size;
776 berkeley_totals(void)
778 uint64_t grand_total;
780 grand_total = text_size_total + data_size_total + bss_size_total;
782 tbl_print_num(text_size_total, radix, 0);
783 tbl_print_num(data_size_total, radix, 1);
784 tbl_print_num(bss_size_total, radix, 2);
785 if (radix == RADIX_OCTAL)
786 tbl_print_num(grand_total, RADIX_OCTAL, 3);
788 tbl_print_num(grand_total, RADIX_DECIMAL, 3);
789 tbl_print_num(grand_total, RADIX_HEX, 4);
793 berkeley_footer(const char *name, const char *ar_name, const char *msg)
797 total_size = text_size + data_size + bss_size;
799 text_size_total += text_size;
800 bss_size_total += bss_size;
801 data_size_total += data_size;
805 tbl_print_num(text_size, radix, 0);
806 tbl_print_num(data_size, radix, 1);
807 tbl_print_num(bss_size, radix, 2);
808 if (radix == RADIX_OCTAL)
809 tbl_print_num(total_size, RADIX_OCTAL, 3);
811 tbl_print_num(total_size, RADIX_DECIMAL, 3);
812 tbl_print_num(total_size, RADIX_HEX, 4);
813 if (ar_name != NULL && name != NULL)
814 (void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg,
816 else if (ar_name != NULL && name == NULL)
817 (void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg);
819 (void) snprintf(buf, BUF_SIZE, "%s", name);
830 if ((tb = calloc(1, sizeof(*tb))) == NULL)
831 err(EXIT_FAILURE, "calloc");
832 if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL)
833 err(EXIT_FAILURE, "calloc");
834 if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL)
835 err(EXIT_FAILURE, "calloc");
841 tbl_print(const char *s, int col)
845 assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col);
846 assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL);
847 if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL)
848 err(EXIT_FAILURE, "strdup");
850 if (len > tb->width[col])
851 tb->width[col] = len;
855 tbl_print_num(uint64_t num, enum radix_style rad, int col)
859 (void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" :
860 ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num);
869 assert(tb != NULL && tb->col > 0);
871 for (i = 0; i < tb->col; i++) {
872 tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row);
873 if (tb->tbl[i] == NULL)
874 err(EXIT_FAILURE, "realloc");
875 tb->tbl[i][tb->row - 1] = NULL;
889 for (i = 0; i < tb->row; i++) {
890 if (style == STYLE_BERKELEY)
892 for (j = 0; j < tb->col; j++) {
893 str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : "");
894 if (style == STYLE_SYSV && j == 0)
895 printf("%-*s", tb->width[j], str);
896 else if (style == STYLE_BERKELEY && j == tb->col - 1)
899 printf("%*s", tb->width[j], str);
907 for (i = 0; i < tb->col; i++) {
908 for (j = 0; j < tb->row; j++) {
920 #define USAGE_MESSAGE "\
921 Usage: %s [options] file ...\n\
922 Display sizes of ELF sections.\n\n\
924 --format=format Display output in specified format. Supported\n\
925 values are `berkeley' and `sysv'.\n\
926 --help Display this help message and exit.\n\
927 --radix=radix Display numeric values in the specified radix.\n\
928 Supported values are: 8, 10 and 16.\n\
929 --totals Show cumulative totals of section sizes.\n\
930 --version Display a version identifier and exit.\n\
931 -A Equivalent to `--format=sysv'.\n\
932 -B Equivalent to `--format=berkeley'.\n\
933 -V Equivalent to `--version'.\n\
934 -d Equivalent to `--radix=10'.\n\
935 -h Same as option --help.\n\
936 -o Equivalent to `--radix=8'.\n\
937 -t Equivalent to option --totals.\n\
938 -x Equivalent to `--radix=16'.\n"
943 (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
950 (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());