diff options
Diffstat (limited to 'contrib/elftoolchain/size/size.c')
-rw-r--r-- | contrib/elftoolchain/size/size.c | 917 |
1 files changed, 917 insertions, 0 deletions
diff --git a/contrib/elftoolchain/size/size.c b/contrib/elftoolchain/size/size.c new file mode 100644 index 000000000000..40d75f8c43f7 --- /dev/null +++ b/contrib/elftoolchain/size/size.c @@ -0,0 +1,917 @@ +/*- + * Copyright (c) 2007 S.Sam Arun Raj + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <assert.h> +#include <err.h> +#include <fcntl.h> +#include <gelf.h> +#include <getopt.h> +#include <libelftc.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "_elftc.h" + +ELFTC_VCSID("$Id: size.c 3183 2015-04-10 16:18:42Z emaste $"); + +#define BUF_SIZE 1024 +#define ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1)) +#define SIZE_VERSION_STRING "size 1.0" + +enum return_code { + RETURN_OK, + RETURN_NOINPUT, + RETURN_DATAERR, + RETURN_USAGE +}; + +enum output_style { + STYLE_BERKELEY, + STYLE_SYSV +}; + +enum radix_style { + RADIX_OCTAL, + RADIX_DECIMAL, + RADIX_HEX +}; + +static uint64_t bss_size, data_size, text_size, total_size; +static uint64_t bss_size_total, data_size_total, text_size_total; +static int show_totals; +static int size_option; +static enum radix_style radix = RADIX_DECIMAL; +static enum output_style style = STYLE_BERKELEY; +static const char *default_args[2] = { "a.out", NULL }; + +static struct { + int row; + int col; + int *width; + char ***tbl; +} *tb; + +enum { + OPT_FORMAT, + OPT_RADIX +}; + +static struct option size_longopts[] = { + { "format", required_argument, &size_option, OPT_FORMAT }, + { "help", no_argument, NULL, 'h' }, + { "radix", required_argument, &size_option, OPT_RADIX }, + { "totals", no_argument, NULL, 't' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } +}; + +static void berkeley_calc(GElf_Shdr *); +static void berkeley_footer(const char *, const char *, const char *); +static void berkeley_header(void); +static void berkeley_totals(void); +static int handle_core(char const *, Elf *elf, GElf_Ehdr *); +static void handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **); +static int handle_elf(char const *); +static void handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t, + const char *); +static void show_version(void); +static void sysv_header(const char *, Elf_Arhdr *); +static void sysv_footer(void); +static void sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *); +static void usage(void); +static void tbl_new(int); +static void tbl_print(const char *, int); +static void tbl_print_num(uint64_t, enum radix_style, int); +static void tbl_append(void); +static void tbl_flush(void); + +/* + * size utility using elf(3) and gelf(3) API to list section sizes and + * total in elf files. Supports only elf files (core dumps in elf + * included) that can be opened by libelf, other formats are not supported. + */ +int +main(int argc, char **argv) +{ + int ch, r, rc; + const char **files, *fn; + + rc = RETURN_OK; + + if (elf_version(EV_CURRENT) == EV_NONE) + errx(EXIT_FAILURE, "ELF library initialization failed: %s", + elf_errmsg(-1)); + + while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts, + NULL)) != -1) + switch((char)ch) { + case 'A': + style = STYLE_SYSV; + break; + case 'B': + style = STYLE_BERKELEY; + break; + case 'V': + show_version(); + break; + case 'd': + radix = RADIX_DECIMAL; + break; + case 'o': + radix = RADIX_OCTAL; + break; + case 't': + show_totals = 1; + break; + case 'x': + radix = RADIX_HEX; + break; + case 0: + switch (size_option) { + case OPT_FORMAT: + if (*optarg == 's' || *optarg == 'S') + style = STYLE_SYSV; + else if (*optarg == 'b' || *optarg == 'B') + style = STYLE_BERKELEY; + else { + warnx("unrecognized format \"%s\".", + optarg); + usage(); + } + break; + case OPT_RADIX: + r = strtol(optarg, NULL, 10); + if (r == 8) + radix = RADIX_OCTAL; + else if (r == 10) + radix = RADIX_DECIMAL; + else if (r == 16) + radix = RADIX_HEX; + else { + warnx("unsupported radix \"%s\".", + optarg); + usage(); + } + break; + default: + err(EXIT_FAILURE, "Error in option handling."); + /*NOTREACHED*/ + } + break; + case 'h': + case '?': + default: + usage(); + /* NOTREACHED */ + } + argc -= optind; + argv += optind; + + files = (argc == 0) ? default_args : (void *) argv; + + while ((fn = *files) != NULL) { + rc = handle_elf(fn); + if (rc != RETURN_OK) + warnx(rc == RETURN_NOINPUT ? + "'%s': No such file" : + "%s: File format not recognized", fn); + files++; + } + if (style == STYLE_BERKELEY) { + if (show_totals) + berkeley_totals(); + tbl_flush(); + } + return (rc); +} + +static Elf_Data * +xlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst, + Elf_Type type, size_t size) +{ + Elf_Data src, dst; + + src.d_buf = _src; + src.d_type = type; + src.d_version = elfhdr->e_version; + src.d_size = size; + dst.d_buf = _dst; + dst.d_version = elfhdr->e_version; + dst.d_size = size; + return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA])); +} + +#define NOTE_OFFSET_32(nhdr, namesz, offset) \ + ((char *)nhdr + sizeof(Elf32_Nhdr) + \ + ELF_ALIGN((int32_t)namesz, 4) + offset) + +#define NOTE_OFFSET_64(nhdr, namesz, offset) \ + ((char *)nhdr + sizeof(Elf32_Nhdr) + \ + ELF_ALIGN((int32_t)namesz, 8) + offset) + +#define PID32(nhdr, namesz, offset) \ + (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr, \ + namesz, offset))); + +#define PID64(nhdr, namesz, offset) \ + (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr, \ + namesz, offset))); + +#define NEXT_NOTE(elfhdr, descsz, namesz, offset) do { \ + if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { \ + offset += ELF_ALIGN((int32_t)descsz, 4) + \ + sizeof(Elf32_Nhdr) + \ + ELF_ALIGN((int32_t)namesz, 4); \ + } else { \ + offset += ELF_ALIGN((int32_t)descsz, 8) + \ + sizeof(Elf32_Nhdr) + \ + ELF_ALIGN((int32_t)namesz, 8); \ + } \ +} while (0) + +/* + * Parse individual note entries inside a PT_NOTE segment. + */ +static void +handle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, + char **cmd_line) +{ + size_t max_size; + uint64_t raw_size; + GElf_Off offset; + static pid_t pid; + uintptr_t ver; + Elf32_Nhdr *nhdr, nhdr_l; + static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/; + char buf[BUF_SIZE], *data, *name; + + if (elf == NULL || elfhdr == NULL || phdr == NULL) + return; + + data = elf_rawfile(elf, &max_size); + offset = phdr->p_offset; + while (data != NULL && offset < phdr->p_offset + phdr->p_filesz) { + nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset); + memset(&nhdr_l, 0, sizeof(Elf32_Nhdr)); + if (!xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type, + ELF_T_WORD, sizeof(Elf32_Word)) || + !xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz, + ELF_T_WORD, sizeof(Elf32_Word)) || + !xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz, + ELF_T_WORD, sizeof(Elf32_Word))) + break; + + name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr)); + switch (nhdr_l.n_type) { + case NT_PRSTATUS: { + raw_size = 0; + if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD && + nhdr_l.n_namesz == 0x8 && + !strcmp(name,"FreeBSD")) { + if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { + raw_size = (uint64_t)*((uint32_t *) + (uintptr_t)(name + + ELF_ALIGN((int32_t) + nhdr_l.n_namesz, 4) + 8)); + ver = (uintptr_t)NOTE_OFFSET_32(nhdr, + nhdr_l.n_namesz,0); + if (*((int *)ver) == 1) + pid = PID32(nhdr, + nhdr_l.n_namesz, 24); + } else { + raw_size = *((uint64_t *)(uintptr_t) + (name + ELF_ALIGN((int32_t) + nhdr_l.n_namesz, 8) + 16)); + ver = (uintptr_t)NOTE_OFFSET_64(nhdr, + nhdr_l.n_namesz,0); + if (*((int *)ver) == 1) + pid = PID64(nhdr, + nhdr_l.n_namesz, 40); + } + xlatetom(elf, elfhdr, &raw_size, &raw_size, + ELF_T_WORD, sizeof(uint64_t)); + xlatetom(elf, elfhdr, &pid, &pid, ELF_T_WORD, + sizeof(pid_t)); + } + + if (raw_size != 0 && style == STYLE_SYSV) { + (void) snprintf(buf, BUF_SIZE, "%s/%d", + ".reg", pid); + tbl_append(); + tbl_print(buf, 0); + tbl_print_num(raw_size, radix, 1); + tbl_print_num(0, radix, 2); + if (!reg_pseudo) { + tbl_append(); + tbl_print(".reg", 0); + tbl_print_num(raw_size, radix, 1); + tbl_print_num(0, radix, 2); + reg_pseudo = 1; + text_size_total += raw_size; + } + text_size_total += raw_size; + } + } + break; + case NT_FPREGSET: /* same as NT_PRFPREG */ + if (style == STYLE_SYSV) { + (void) snprintf(buf, BUF_SIZE, + "%s/%d", ".reg2", pid); + tbl_append(); + tbl_print(buf, 0); + tbl_print_num(nhdr_l.n_descsz, radix, 1); + tbl_print_num(0, radix, 2); + if (!reg2_pseudo) { + tbl_append(); + tbl_print(".reg2", 0); + tbl_print_num(nhdr_l.n_descsz, radix, + 1); + tbl_print_num(0, radix, 2); + reg2_pseudo = 1; + text_size_total += nhdr_l.n_descsz; + } + text_size_total += nhdr_l.n_descsz; + } + break; +#if 0 + case NT_AUXV: + if (style == STYLE_SYSV) { + tbl_append(); + tbl_print(".auxv", 0); + tbl_print_num(nhdr_l.n_descsz, radix, 1); + tbl_print_num(0, radix, 2); + text_size_total += nhdr_l.n_descsz; + } + break; + case NT_PRXFPREG: + if (style == STYLE_SYSV) { + (void) snprintf(buf, BUF_SIZE, "%s/%d", + ".reg-xfp", pid); + tbl_append(); + tbl_print(buf, 0); + tbl_print_num(nhdr_l.n_descsz, radix, 1); + tbl_print_num(0, radix, 2); + if (!regxfp_pseudo) { + tbl_append(); + tbl_print(".reg-xfp", 0); + tbl_print_num(nhdr_l.n_descsz, radix, + 1); + tbl_print_num(0, radix, 2); + regxfp_pseudo = 1; + text_size_total += nhdr_l.n_descsz; + } + text_size_total += nhdr_l.n_descsz; + } + break; + case NT_PSINFO: +#endif + case NT_PRPSINFO: { + /* FreeBSD 64-bit */ + if (nhdr_l.n_descsz == 0x78 && + !strcmp(name,"FreeBSD")) { + *cmd_line = strdup(NOTE_OFFSET_64(nhdr, + nhdr_l.n_namesz, 33)); + /* FreeBSD 32-bit */ + } else if (nhdr_l.n_descsz == 0x6c && + !strcmp(name,"FreeBSD")) { + *cmd_line = strdup(NOTE_OFFSET_32(nhdr, + nhdr_l.n_namesz, 25)); + } + /* Strip any trailing spaces */ + if (*cmd_line != NULL) { + char *s; + + s = *cmd_line + strlen(*cmd_line); + while (s > *cmd_line) { + if (*(s-1) != 0x20) break; + s--; + } + *s = 0; + } + break; + } +#if 0 + case NT_PSTATUS: + case NT_LWPSTATUS: +#endif + default: + break; + } + NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset); + } +} + +/* + * Handles program headers except for PT_NOTE, when sysv output stlye is + * choosen, prints out the segment name and length. For berkely output + * style only PT_LOAD segments are handled, and text, + * data, bss size is calculated for them. + */ +static void +handle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, + uint32_t idx, const char *name) +{ + uint64_t addr, size; + int split; + char buf[BUF_SIZE]; + + if (elf == NULL || elfhdr == NULL || phdr == NULL) + return; + + size = addr = 0; + split = (phdr->p_memsz > 0) && (phdr->p_filesz > 0) && + (phdr->p_memsz > phdr->p_filesz); + + if (style == STYLE_SYSV) { + (void) snprintf(buf, BUF_SIZE, + "%s%d%s", name, idx, (split ? "a" : "")); + tbl_append(); + tbl_print(buf, 0); + tbl_print_num(phdr->p_filesz, radix, 1); + tbl_print_num(phdr->p_vaddr, radix, 2); + text_size_total += phdr->p_filesz; + if (split) { + size = phdr->p_memsz - phdr->p_filesz; + addr = phdr->p_vaddr + phdr->p_filesz; + (void) snprintf(buf, BUF_SIZE, "%s%d%s", name, + idx, "b"); + text_size_total += phdr->p_memsz - phdr->p_filesz; + tbl_append(); + tbl_print(buf, 0); + tbl_print_num(size, radix, 1); + tbl_print_num(addr, radix, 2); + } + } else { + if (phdr->p_type != PT_LOAD) + return; + if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) { + data_size += phdr->p_filesz; + if (split) + data_size += phdr->p_memsz - phdr->p_filesz; + } else { + text_size += phdr->p_filesz; + if (split) + text_size += phdr->p_memsz - phdr->p_filesz; + } + } +} + +/* + * Given a core dump file, this function maps program headers to segments. + */ +static int +handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr) +{ + GElf_Phdr phdr; + uint32_t i; + char *core_cmdline; + const char *seg_name; + + if (name == NULL || elf == NULL || elfhdr == NULL) + return (RETURN_DATAERR); + if (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE) + return (RETURN_DATAERR); + + seg_name = core_cmdline = NULL; + if (style == STYLE_SYSV) + sysv_header(name, NULL); + else + berkeley_header(); + + for (i = 0; i < elfhdr->e_phnum; i++) { + if (gelf_getphdr(elf, i, &phdr) != NULL) { + if (phdr.p_type == PT_NOTE) { + handle_phdr(elf, elfhdr, &phdr, i, "note"); + handle_core_note(elf, elfhdr, &phdr, + &core_cmdline); + } else { + switch(phdr.p_type) { + case PT_NULL: + seg_name = "null"; + break; + case PT_LOAD: + seg_name = "load"; + break; + case PT_DYNAMIC: + seg_name = "dynamic"; + break; + case PT_INTERP: + seg_name = "interp"; + break; + case PT_SHLIB: + seg_name = "shlib"; + break; + case PT_PHDR: + seg_name = "phdr"; + break; + case PT_GNU_EH_FRAME: + seg_name = "eh_frame_hdr"; + break; + case PT_GNU_STACK: + seg_name = "stack"; + break; + default: + seg_name = "segment"; + } + handle_phdr(elf, elfhdr, &phdr, i, seg_name); + } + } + } + + if (style == STYLE_BERKELEY) { + if (core_cmdline != NULL) { + berkeley_footer(core_cmdline, name, + "core file invoked as"); + } else { + berkeley_footer(core_cmdline, name, "core file"); + } + } else { + sysv_footer(); + if (core_cmdline != NULL) { + (void) printf(" (core file invoked as %s)\n\n", + core_cmdline); + } else { + (void) printf(" (core file)\n\n"); + } + } + free(core_cmdline); + return (RETURN_OK); +} + +/* + * Given an elf object,ar(1) filename, and based on the output style + * and radix format the various sections and their length will be printed + * or the size of the text, data, bss sections will be printed out. + */ +static int +handle_elf(char const *name) +{ + GElf_Ehdr elfhdr; + GElf_Shdr shdr; + Elf *elf, *elf1; + Elf_Arhdr *arhdr; + Elf_Scn *scn; + Elf_Cmd elf_cmd; + int exit_code, fd; + + if (name == NULL) + return (RETURN_NOINPUT); + + if ((fd = open(name, O_RDONLY, 0)) < 0) + return (RETURN_NOINPUT); + + elf_cmd = ELF_C_READ; + elf1 = elf_begin(fd, elf_cmd, NULL); + while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) { + arhdr = elf_getarhdr(elf); + if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) { + (void) elf_end(elf); + (void) elf_end(elf1); + (void) close(fd); + return (RETURN_DATAERR); + } + if (elf_kind(elf) != ELF_K_ELF || + (gelf_getehdr(elf, &elfhdr) == NULL)) { + elf_cmd = elf_next(elf); + (void) elf_end(elf); + warnx("%s: File format not recognized", + arhdr->ar_name); + continue; + } + /* Core dumps are handled separately */ + if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) { + exit_code = handle_core(name, elf, &elfhdr); + (void) elf_end(elf); + (void) elf_end(elf1); + (void) close(fd); + return (exit_code); + } else { + scn = NULL; + if (style == STYLE_BERKELEY) { + berkeley_header(); + while ((scn = elf_nextscn(elf, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) != NULL) + berkeley_calc(&shdr); + } + } else { + sysv_header(name, arhdr); + scn = NULL; + while ((scn = elf_nextscn(elf, scn)) != NULL) { + if (gelf_getshdr(scn, &shdr) != NULL) + sysv_calc(elf, &elfhdr, &shdr); + } + } + if (style == STYLE_BERKELEY) { + if (arhdr != NULL) { + berkeley_footer(name, arhdr->ar_name, + "ex"); + } else { + berkeley_footer(name, NULL, "ex"); + } + } else { + sysv_footer(); + } + } + elf_cmd = elf_next(elf); + (void) elf_end(elf); + } + (void) elf_end(elf1); + (void) close(fd); + return (RETURN_OK); +} + +/* + * Sysv formatting helper functions. + */ +static void +sysv_header(const char *name, Elf_Arhdr *arhdr) +{ + + text_size_total = 0; + if (arhdr != NULL) + (void) printf("%s (ex %s):\n", arhdr->ar_name, name); + else + (void) printf("%s :\n", name); + tbl_new(3); + tbl_append(); + tbl_print("section", 0); + tbl_print("size", 1); + tbl_print("addr", 2); +} + +static void +sysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr) +{ + char *section_name; + + section_name = elf_strptr(elf, elfhdr->e_shstrndx, + (size_t) shdr->sh_name); + if ((shdr->sh_type == SHT_SYMTAB || + shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA || + shdr->sh_type == SHT_REL) && shdr->sh_addr == 0) + return; + tbl_append(); + tbl_print(section_name, 0); + tbl_print_num(shdr->sh_size, radix, 1); + tbl_print_num(shdr->sh_addr, radix, 2); + text_size_total += shdr->sh_size; +} + +static void +sysv_footer(void) +{ + tbl_append(); + tbl_print("Total", 0); + tbl_print_num(text_size_total, radix, 1); + tbl_flush(); + putchar('\n'); +} + +/* + * berkeley style output formatting helper functions. + */ +static void +berkeley_header(void) +{ + static int printed; + + text_size = data_size = bss_size = 0; + if (!printed) { + tbl_new(6); + tbl_append(); + tbl_print("text", 0); + tbl_print("data", 1); + tbl_print("bss", 2); + if (radix == RADIX_OCTAL) + tbl_print("oct", 3); + else + tbl_print("dec", 3); + tbl_print("hex", 4); + tbl_print("filename", 5); + printed = 1; + } +} + +static void +berkeley_calc(GElf_Shdr *shdr) +{ + if (shdr != NULL) { + if (!(shdr->sh_flags & SHF_ALLOC)) + return; + if ((shdr->sh_flags & SHF_ALLOC) && + ((shdr->sh_flags & SHF_EXECINSTR) || + !(shdr->sh_flags & SHF_WRITE))) + text_size += shdr->sh_size; + else if ((shdr->sh_flags & SHF_ALLOC) && + (shdr->sh_flags & SHF_WRITE) && + (shdr->sh_type != SHT_NOBITS)) + data_size += shdr->sh_size; + else + bss_size += shdr->sh_size; + } +} + +static void +berkeley_totals(void) +{ + long unsigned int grand_total; + + grand_total = text_size_total + data_size_total + bss_size_total; + tbl_append(); + tbl_print_num(text_size_total, radix, 0); + tbl_print_num(data_size_total, radix, 1); + tbl_print_num(bss_size_total, radix, 2); + if (radix == RADIX_OCTAL) + tbl_print_num(grand_total, RADIX_OCTAL, 3); + else + tbl_print_num(grand_total, RADIX_DECIMAL, 3); + tbl_print_num(grand_total, RADIX_HEX, 4); +} + +static void +berkeley_footer(const char *name, const char *ar_name, const char *msg) +{ + char buf[BUF_SIZE]; + + total_size = text_size + data_size + bss_size; + if (show_totals) { + text_size_total += text_size; + bss_size_total += bss_size; + data_size_total += data_size; + } + + tbl_append(); + tbl_print_num(text_size, radix, 0); + tbl_print_num(data_size, radix, 1); + tbl_print_num(bss_size, radix, 2); + if (radix == RADIX_OCTAL) + tbl_print_num(total_size, RADIX_OCTAL, 3); + else + tbl_print_num(total_size, RADIX_DECIMAL, 3); + tbl_print_num(total_size, RADIX_HEX, 4); + if (ar_name != NULL && name != NULL) + (void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg, + name); + else if (ar_name != NULL && name == NULL) + (void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg); + else + (void) snprintf(buf, BUF_SIZE, "%s", name); + tbl_print(buf, 5); +} + + +static void +tbl_new(int col) +{ + + assert(tb == NULL); + assert(col > 0); + if ((tb = calloc(1, sizeof(*tb))) == NULL) + err(EXIT_FAILURE, "calloc"); + if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL) + err(EXIT_FAILURE, "calloc"); + if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL) + err(EXIT_FAILURE, "calloc"); + tb->col = col; + tb->row = 0; +} + +static void +tbl_print(const char *s, int col) +{ + int len; + + assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col); + assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL); + if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL) + err(EXIT_FAILURE, "strdup"); + len = strlen(s); + if (len > tb->width[col]) + tb->width[col] = len; +} + +static void +tbl_print_num(uint64_t num, enum radix_style rad, int col) +{ + char buf[BUF_SIZE]; + + (void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" : + ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num); + tbl_print(buf, col); +} + +static void +tbl_append(void) +{ + int i; + + assert(tb != NULL && tb->col > 0); + tb->row++; + for (i = 0; i < tb->col; i++) { + tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row); + if (tb->tbl[i] == NULL) + err(EXIT_FAILURE, "realloc"); + tb->tbl[i][tb->row - 1] = NULL; + } +} + +static void +tbl_flush(void) +{ + const char *str; + int i, j; + + if (tb == NULL) + return; + + assert(tb->col > 0); + for (i = 0; i < tb->row; i++) { + if (style == STYLE_BERKELEY) + printf(" "); + for (j = 0; j < tb->col; j++) { + str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : ""); + if (style == STYLE_SYSV && j == 0) + printf("%-*s", tb->width[j], str); + else if (style == STYLE_BERKELEY && j == tb->col - 1) + printf("%s", str); + else + printf("%*s", tb->width[j], str); + if (j == tb->col -1) + putchar('\n'); + else + printf(" "); + } + } + + for (i = 0; i < tb->col; i++) { + for (j = 0; j < tb->row; j++) { + if (tb->tbl[i][j]) + free(tb->tbl[i][j]); + } + free(tb->tbl[i]); + } + free(tb->tbl); + free(tb->width); + free(tb); + tb = NULL; +} + +#define USAGE_MESSAGE "\ +Usage: %s [options] file ...\n\ + Display sizes of ELF sections.\n\n\ + Options:\n\ + --format=format Display output in specified format. Supported\n\ + values are `berkeley' and `sysv'.\n\ + --help Display this help message and exit.\n\ + --radix=radix Display numeric values in the specified radix.\n\ + Supported values are: 8, 10 and 16.\n\ + --totals Show cumulative totals of section sizes.\n\ + --version Display a version identifier and exit.\n\ + -A Equivalent to `--format=sysv'.\n\ + -B Equivalent to `--format=berkeley'.\n\ + -V Equivalent to `--version'.\n\ + -d Equivalent to `--radix=10'.\n\ + -h Same as option --help.\n\ + -o Equivalent to `--radix=8'.\n\ + -t Equivalent to option --totals.\n\ + -x Equivalent to `--radix=16'.\n" + +static void +usage(void) +{ + (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); + exit(EXIT_FAILURE); +} + +static void +show_version(void) +{ + (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version()); + exit(EXIT_SUCCESS); +} |