1121474Sume/*- 262614Sitojun * Copyright (c) 2007 S.Sam Arun Raj 355163Sshin * All rights reserved. 455163Sshin * 555163Sshin * Redistribution and use in source and binary forms, with or without 655163Sshin * modification, are permitted provided that the following conditions 755163Sshin * are met: 855163Sshin * 1. Redistributions of source code must retain the above copyright 955163Sshin * notice, this list of conditions and the following disclaimer. 1055163Sshin * 2. Redistributions in binary form must reproduce the above copyright 1155163Sshin * notice, this list of conditions and the following disclaimer in the 1255163Sshin * documentation and/or other materials provided with the distribution. 1355163Sshin * 1455163Sshin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1555163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1655163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1755163Sshin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1855163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1955163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2055163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2155163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2255163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2355163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2455163Sshin * SUCH DAMAGE. 2555163Sshin */ 2655163Sshin 2755163Sshin#include <assert.h> 2855163Sshin#include <err.h> 2955163Sshin#include <fcntl.h> 3055163Sshin#include <gelf.h> 3155163Sshin#include <getopt.h> 3255163Sshin#include <libelftc.h> 3355163Sshin#include <stdint.h> 3455163Sshin#include <stdio.h> 3555163Sshin#include <stdlib.h> 3655163Sshin#include <string.h> 3755163Sshin#include <unistd.h> 3855163Sshin 3955163Sshin#include "_elftc.h" 4061877Sume 41105940SumeELFTC_VCSID("$Id: size.c 3458 2016-05-09 15:01:25Z emaste $"); 42105940Sume 4356627Sshin#define BUF_SIZE 1024 4456627Sshin#define ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1)) 4556627Sshin#define SIZE_VERSION_STRING "size 1.0" 4656627Sshin 4756627Sshinenum return_code { 4861877Sume RETURN_OK, 4961877Sume RETURN_NOINPUT, 5061877Sume RETURN_DATAERR, 5161877Sume RETURN_USAGE 52105940Sume}; 53105940Sume 5461877Sumeenum output_style { 5561877Sume STYLE_BERKELEY, 5661877Sume STYLE_SYSV 5761877Sume}; 58105940Sume 59105940Sumeenum radix_style { 60105940Sume RADIX_OCTAL, 61105940Sume RADIX_DECIMAL, 62105940Sume RADIX_HEX 6355163Sshin}; 6455163Sshin 6592986Sobrienstatic uint64_t bss_size, data_size, text_size, total_size; 6692986Sobrienstatic uint64_t bss_size_total, data_size_total, text_size_total; 6792986Sobrienstatic int show_totals; 6871579Sdeischenstatic int size_option; 6955163Sshinstatic enum radix_style radix = RADIX_DECIMAL; 7055163Sshinstatic enum output_style style = STYLE_BERKELEY; 7155163Sshinstatic const char *default_args[2] = { "a.out", NULL }; 7255163Sshin 7355163Sshinstatic struct { 74121747Sume int row; 75121747Sume int col; 76121747Sume int *width; 77121747Sume char ***tbl; 78121747Sume} *tb; 79121747Sume 8055163Sshinenum { 8155163Sshin OPT_FORMAT, 82121474Sume OPT_RADIX 83121474Sume}; 84121474Sume 8555163Sshinstatic struct option size_longopts[] = { 86114681Sdeischen { "format", required_argument, &size_option, OPT_FORMAT }, 8755163Sshin { "help", no_argument, NULL, 'h' }, 8855163Sshin { "radix", required_argument, &size_option, OPT_RADIX }, 8955163Sshin { "totals", no_argument, NULL, 't' }, 9055163Sshin { "version", no_argument, NULL, 'V' }, 9155163Sshin { NULL, 0, NULL, 0 } 9255163Sshin}; 9355163Sshin 9461877Sumestatic void berkeley_calc(GElf_Shdr *); 95102237Spirzykstatic void berkeley_footer(const char *, const char *, const char *); 96102237Spirzykstatic void berkeley_header(void); 97102237Spirzykstatic void berkeley_totals(void); 9878012Sumestatic int handle_core(char const *, Elf *elf, GElf_Ehdr *); 9978012Sumestatic void handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **); 10078012Sumestatic int handle_elf(char const *); 10155163Sshinstatic void handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t, 10265532Snectar const char *); 10365532Snectarstatic void show_version(void); 10471579Sdeischenstatic void sysv_header(const char *, Elf_Arhdr *); 105111618Snectarstatic void sysv_footer(void); 10665532Snectarstatic void sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *); 10755163Sshinstatic void usage(void); 10855163Sshinstatic void tbl_new(int); 10955163Sshinstatic void tbl_print(const char *, int); 11055163Sshinstatic void tbl_print_num(uint64_t, enum radix_style, int); 111105940Sumestatic void tbl_append(void); 112105940Sumestatic void tbl_flush(void); 113105940Sume 114105940Sume/* 11555163Sshin * size utility using elf(3) and gelf(3) API to list section sizes and 11655163Sshin * total in elf files. Supports only elf files (core dumps in elf 117105940Sume * included) that can be opened by libelf, other formats are not supported. 118105940Sume */ 11955163Sshinint 12055163Sshinmain(int argc, char **argv) 12155163Sshin{ 12255163Sshin int ch, r, rc; 12355163Sshin const char **files, *fn; 12455163Sshin 125105940Sume rc = RETURN_OK; 12655163Sshin 127121747Sume if (elf_version(EV_CURRENT) == EV_NONE) 128121747Sume errx(EXIT_FAILURE, "ELF library initialization failed: %s", 129121747Sume elf_errmsg(-1)); 130121747Sume 131121747Sume while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts, 132121747Sume NULL)) != -1) 133121747Sume switch((char)ch) { 134121747Sume case 'A': 13555163Sshin style = STYLE_SYSV; 13655163Sshin break; 13755163Sshin case 'B': 13855163Sshin style = STYLE_BERKELEY; 13955163Sshin break; 14055163Sshin case 'V': 14173665Sobrien show_version(); 14255163Sshin break; 14355163Sshin case 'd': 14455163Sshin radix = RADIX_DECIMAL; 14555163Sshin break; 14655163Sshin case 'o': 14755163Sshin radix = RADIX_OCTAL; 14855163Sshin break; 14955163Sshin case 't': 15055163Sshin show_totals = 1; 15155163Sshin break; 15255163Sshin case 'x': 15355163Sshin radix = RADIX_HEX; 15455163Sshin break; 15555163Sshin case 0: 15655163Sshin switch (size_option) { 15755163Sshin case OPT_FORMAT: 15855163Sshin if (*optarg == 's' || *optarg == 'S') 15955163Sshin style = STYLE_SYSV; 16055163Sshin else if (*optarg == 'b' || *optarg == 'B') 16155163Sshin style = STYLE_BERKELEY; 16261877Sume else { 16355163Sshin warnx("unrecognized format \"%s\".", 16455163Sshin optarg); 16555163Sshin usage(); 16655163Sshin } 167105940Sume break; 168105940Sume case OPT_RADIX: 169105940Sume r = strtol(optarg, NULL, 10); 17055163Sshin if (r == 8) 17155163Sshin radix = RADIX_OCTAL; 17255163Sshin else if (r == 10) 17361877Sume radix = RADIX_DECIMAL; 174121474Sume else if (r == 16) 17561877Sume radix = RADIX_HEX; 17661877Sume else { 177121474Sume warnx("unsupported radix \"%s\".", 178121474Sume optarg); 179121474Sume usage(); 18061877Sume } 181121474Sume break; 182121474Sume default: 183121474Sume err(EXIT_FAILURE, "Error in option handling."); 184121474Sume /*NOTREACHED*/ 185121474Sume } 186121474Sume break; 18761877Sume case 'h': 18855163Sshin case '?': 18955163Sshin default: 19055163Sshin usage(); 191105940Sume /* NOTREACHED */ 19255163Sshin } 193105940Sume argc -= optind; 19455163Sshin argv += optind; 19555163Sshin 196121747Sume files = (argc == 0) ? default_args : (void *) argv; 197121747Sume 198121747Sume while ((fn = *files) != NULL) { 199121747Sume rc = handle_elf(fn); 200121747Sume if (rc != RETURN_OK) 201121747Sume warnx(rc == RETURN_NOINPUT ? 202121747Sume "'%s': No such file" : 203121747Sume "%s: File format not recognized", fn); 204121747Sume files++; 205121747Sume } 206121747Sume if (style == STYLE_BERKELEY) { 207121747Sume if (show_totals) 208121747Sume berkeley_totals(); 209121747Sume tbl_flush(); 210121747Sume } 211121747Sume return (rc); 212121747Sume} 21365532Snectar 21465532Snectarstatic Elf_Data * 21565532Snectarxlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst, 21665532Snectar Elf_Type type, size_t size) 21765532Snectar{ 21865532Snectar Elf_Data src, dst; 21961877Sume 22061877Sume src.d_buf = _src; 22161877Sume src.d_type = type; 22262614Sitojun src.d_version = elfhdr->e_version; 22361877Sume src.d_size = size; 22461877Sume dst.d_buf = _dst; 22561877Sume dst.d_version = elfhdr->e_version; 22661877Sume dst.d_size = size; 22761877Sume return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA])); 228121426Sume} 229121426Sume 230121426Sume#define NOTE_OFFSET_32(nhdr, namesz, offset) \ 231121426Sume ((char *)nhdr + sizeof(Elf32_Nhdr) + \ 232121426Sume ELF_ALIGN((int32_t)namesz, 4) + offset) 233121426Sume 234121426Sume#define NOTE_OFFSET_64(nhdr, namesz, offset) \ 23592905Sobrien ((char *)nhdr + sizeof(Elf32_Nhdr) + \ 23692941Sobrien ELF_ALIGN((int32_t)namesz, 8) + offset) 23792941Sobrien 23892941Sobrien#define PID32(nhdr, namesz, offset) \ 239121474Sume (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr, \ 24092941Sobrien namesz, offset))); 24192941Sobrien 24292941Sobrien#define PID64(nhdr, namesz, offset) \ 24392941Sobrien (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr, \ 24492941Sobrien namesz, offset))); 24592941Sobrien 24692905Sobrien#define NEXT_NOTE(elfhdr, descsz, namesz, offset) do { \ 24792905Sobrien if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { \ 24892905Sobrien offset += ELF_ALIGN((int32_t)descsz, 4) + \ 249121474Sume sizeof(Elf32_Nhdr) + \ 250121747Sume ELF_ALIGN((int32_t)namesz, 4); \ 25161877Sume } else { \ 252105943Sume offset += ELF_ALIGN((int32_t)descsz, 8) + \ 25361877Sume sizeof(Elf32_Nhdr) + \ 254121747Sume ELF_ALIGN((int32_t)namesz, 8); \ 25555163Sshin } \ 256121426Sume} while (0) 257121426Sume 258121426Sume/* 259121747Sume * Parse individual note entries inside a PT_NOTE segment. 260121747Sume */ 261121747Sumestatic void 262121747Sumehandle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, 263121747Sume char **cmd_line) 264121747Sume{ 26592941Sobrien size_t max_size, segment_end; 26692941Sobrien uint64_t raw_size; 267121426Sume GElf_Off offset; 268121426Sume static pid_t pid; 269121426Sume uintptr_t ver; 270105943Sume Elf32_Nhdr *nhdr, nhdr_l; 27192905Sobrien static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/; 27292905Sobrien char buf[BUF_SIZE], *data, *name; 27392905Sobrien 27492905Sobrien if (elf == NULL || elfhdr == NULL || phdr == NULL) 27561877Sume return; 27692905Sobrien 27792905Sobrien data = elf_rawfile(elf, &max_size); 27861877Sume offset = phdr->p_offset; 27961877Sume if (offset >= max_size || phdr->p_filesz > max_size - offset) { 28092905Sobrien warnx("invalid PHDR offset"); 28192905Sobrien return; 28292941Sobrien } 28392941Sobrien segment_end = phdr->p_offset + phdr->p_filesz; 28461877Sume 285121347Sume while (data != NULL && offset + sizeof(Elf32_Nhdr) < segment_end) { 286121347Sume nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset); 287121347Sume memset(&nhdr_l, 0, sizeof(Elf32_Nhdr)); 288121347Sume if (!xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type, 289121347Sume ELF_T_WORD, sizeof(Elf32_Word)) || 290121347Sume !xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz, 291121347Sume ELF_T_WORD, sizeof(Elf32_Word)) || 292121347Sume !xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz, 293121347Sume ELF_T_WORD, sizeof(Elf32_Word))) 294121347Sume break; 295121347Sume 296121347Sume if (offset + sizeof(Elf32_Nhdr) + 297121347Sume ELF_ALIGN(nhdr_l.n_namesz, 4) + 298121347Sume ELF_ALIGN(nhdr_l.n_descsz, 4) >= segment_end) { 299121347Sume warnx("invalid note header"); 300121347Sume return; 301121347Sume } 302121347Sume 303121347Sume name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr)); 304121347Sume switch (nhdr_l.n_type) { 30555163Sshin case NT_PRSTATUS: { 30655163Sshin raw_size = 0; 307104558Sume if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD && 308104558Sume nhdr_l.n_namesz == 0x8 && 309104558Sume !strcmp(name,"FreeBSD")) { 310104558Sume if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { 311104558Sume raw_size = (uint64_t)*((uint32_t *) 312104558Sume (uintptr_t)(name + 313114681Sdeischen ELF_ALIGN((int32_t) 314104558Sume nhdr_l.n_namesz, 4) + 8)); 315114681Sdeischen ver = (uintptr_t)NOTE_OFFSET_32(nhdr, 316104558Sume nhdr_l.n_namesz,0); 317114681Sdeischen if (*((int *)ver) == 1) 318104558Sume pid = PID32(nhdr, 31955163Sshin nhdr_l.n_namesz, 24); 32055163Sshin } else { 321105940Sume raw_size = *((uint64_t *)(uintptr_t) 32255163Sshin (name + ELF_ALIGN((int32_t) 32355163Sshin nhdr_l.n_namesz, 8) + 16)); 32455163Sshin ver = (uintptr_t)NOTE_OFFSET_64(nhdr, 32555163Sshin nhdr_l.n_namesz,0); 32655163Sshin if (*((int *)ver) == 1) 32755163Sshin pid = PID64(nhdr, 32855163Sshin nhdr_l.n_namesz, 40); 32961877Sume } 33055163Sshin xlatetom(elf, elfhdr, &raw_size, &raw_size, 331105940Sume ELF_T_WORD, sizeof(uint64_t)); 33255163Sshin xlatetom(elf, elfhdr, &pid, &pid, ELF_T_WORD, 33355163Sshin sizeof(pid_t)); 33455163Sshin } 33555163Sshin 33655163Sshin if (raw_size != 0 && style == STYLE_SYSV) { 33761877Sume (void) snprintf(buf, BUF_SIZE, "%s/%d", 33855163Sshin ".reg", pid); 339105940Sume tbl_append(); 34055163Sshin tbl_print(buf, 0); 34155163Sshin tbl_print_num(raw_size, radix, 1); 34255163Sshin tbl_print_num(0, radix, 2); 34355163Sshin if (!reg_pseudo) { 34455163Sshin tbl_append(); 34561877Sume tbl_print(".reg", 0); 34655163Sshin tbl_print_num(raw_size, radix, 1); 347105940Sume tbl_print_num(0, radix, 2); 34855163Sshin reg_pseudo = 1; 34955163Sshin text_size_total += raw_size; 35055163Sshin } 35155163Sshin text_size_total += raw_size; 35261877Sume } 35361877Sume } 35455163Sshin break; 355105940Sume case NT_FPREGSET: /* same as NT_PRFPREG */ 35661877Sume if (style == STYLE_SYSV) { 357105940Sume (void) snprintf(buf, BUF_SIZE, 35861877Sume "%s/%d", ".reg2", pid); 35955163Sshin tbl_append(); 36055163Sshin tbl_print(buf, 0); 36155163Sshin tbl_print_num(nhdr_l.n_descsz, radix, 1); 36255163Sshin tbl_print_num(0, radix, 2); 36355163Sshin if (!reg2_pseudo) { 364121347Sume tbl_append(); 365121347Sume tbl_print(".reg2", 0); 366121347Sume tbl_print_num(nhdr_l.n_descsz, radix, 367121347Sume 1); 368121347Sume tbl_print_num(0, radix, 2); 369121347Sume reg2_pseudo = 1; 370121348Sume text_size_total += nhdr_l.n_descsz; 37155163Sshin } 37255163Sshin text_size_total += nhdr_l.n_descsz; 37355163Sshin } 37455163Sshin break; 37555163Sshin#if 0 37655163Sshin case NT_AUXV: 37755163Sshin if (style == STYLE_SYSV) { 37855163Sshin tbl_append(); 37955163Sshin tbl_print(".auxv", 0); 38055163Sshin tbl_print_num(nhdr_l.n_descsz, radix, 1); 38155163Sshin tbl_print_num(0, radix, 2); 38255163Sshin text_size_total += nhdr_l.n_descsz; 38355163Sshin } 38455163Sshin break; 38561877Sume case NT_PRXFPREG: 38661877Sume if (style == STYLE_SYSV) { 38755163Sshin (void) snprintf(buf, BUF_SIZE, "%s/%d", 38855163Sshin ".reg-xfp", pid); 38955163Sshin tbl_append(); 39055163Sshin tbl_print(buf, 0); 39155163Sshin tbl_print_num(nhdr_l.n_descsz, radix, 1); 39255163Sshin tbl_print_num(0, radix, 2); 39362836Sitojun if (!regxfp_pseudo) { 39462836Sitojun tbl_append(); 39562836Sitojun tbl_print(".reg-xfp", 0); 39662836Sitojun tbl_print_num(nhdr_l.n_descsz, radix, 39762836Sitojun 1); 398105943Sume tbl_print_num(0, radix, 2); 39962836Sitojun regxfp_pseudo = 1; 400105943Sume text_size_total += nhdr_l.n_descsz; 40162836Sitojun } 40262836Sitojun text_size_total += nhdr_l.n_descsz; 40362836Sitojun } 40455163Sshin break; 40555163Sshin case NT_PSINFO: 40655163Sshin#endif 40755163Sshin case NT_PRPSINFO: { 40855163Sshin /* FreeBSD 64-bit */ 40955163Sshin if (nhdr_l.n_descsz == 0x78 && 41055163Sshin !strcmp(name,"FreeBSD")) { 41155163Sshin *cmd_line = strdup(NOTE_OFFSET_64(nhdr, 41255163Sshin nhdr_l.n_namesz, 33)); 41355163Sshin /* FreeBSD 32-bit */ 41455163Sshin } else if (nhdr_l.n_descsz == 0x6c && 415121474Sume !strcmp(name,"FreeBSD")) { 416121474Sume *cmd_line = strdup(NOTE_OFFSET_32(nhdr, 41755163Sshin nhdr_l.n_namesz, 25)); 41855163Sshin } 419121747Sume /* Strip any trailing spaces */ 42055163Sshin if (*cmd_line != NULL) { 42161877Sume char *s; 42255163Sshin 42355163Sshin s = *cmd_line + strlen(*cmd_line); 42455163Sshin while (s > *cmd_line) { 42555163Sshin if (*(s-1) != 0x20) break; 42655163Sshin s--; 42755163Sshin } 42855163Sshin *s = 0; 42955163Sshin } 43055163Sshin break; 43155163Sshin } 43255163Sshin#if 0 43355163Sshin case NT_PSTATUS: 43455163Sshin case NT_LWPSTATUS: 43555163Sshin#endif 43655163Sshin default: 43755163Sshin break; 43855163Sshin } 43955163Sshin NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset); 44055163Sshin } 44155163Sshin} 44255163Sshin 44355163Sshin/* 44455163Sshin * Handles program headers except for PT_NOTE, when sysv output style is 44555163Sshin * chosen, prints out the segment name and length. For berkely output 44655163Sshin * style only PT_LOAD segments are handled, and text, 44755163Sshin * data, bss size is calculated for them. 44855163Sshin */ 44955163Sshinstatic void 45055163Sshinhandle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, 45155163Sshin uint32_t idx, const char *name) 45255163Sshin{ 45355163Sshin uint64_t addr, size; 45455163Sshin int split; 45555163Sshin char buf[BUF_SIZE]; 45655163Sshin 45755163Sshin if (elf == NULL || elfhdr == NULL || phdr == NULL) 45855163Sshin return; 45961877Sume 460121474Sume split = (phdr->p_memsz > 0) && (phdr->p_filesz > 0) && 46161877Sume (phdr->p_memsz > phdr->p_filesz); 462121474Sume 46355163Sshin if (style == STYLE_SYSV) { 464121474Sume (void) snprintf(buf, BUF_SIZE, 46555163Sshin "%s%d%s", name, idx, (split ? "a" : "")); 466121474Sume tbl_append(); 467121474Sume tbl_print(buf, 0); 468121474Sume tbl_print_num(phdr->p_filesz, radix, 1); 469121474Sume tbl_print_num(phdr->p_vaddr, radix, 2); 47055163Sshin text_size_total += phdr->p_filesz; 47155163Sshin if (split) { 47255163Sshin size = phdr->p_memsz - phdr->p_filesz; 47355163Sshin addr = phdr->p_vaddr + phdr->p_filesz; 47461877Sume (void) snprintf(buf, BUF_SIZE, "%s%d%s", name, 47561877Sume idx, "b"); 476105940Sume text_size_total += phdr->p_memsz - phdr->p_filesz; 47761877Sume tbl_append(); 47861877Sume tbl_print(buf, 0); 47961877Sume tbl_print_num(size, radix, 1); 48061877Sume tbl_print_num(addr, radix, 2); 48161877Sume } 48261877Sume } else { 48361877Sume if (phdr->p_type != PT_LOAD) 48461877Sume return; 48561877Sume if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) { 48661877Sume data_size += phdr->p_filesz; 48761877Sume if (split) 48861877Sume data_size += phdr->p_memsz - phdr->p_filesz; 48961877Sume } else { 49061877Sume text_size += phdr->p_filesz; 49161877Sume if (split) 49261877Sume text_size += phdr->p_memsz - phdr->p_filesz; 49361877Sume } 49455163Sshin } 49555163Sshin} 49661877Sume 49761877Sume/* 49861877Sume * Given a core dump file, this function maps program headers to segments. 49955163Sshin */ 50055163Sshinstatic int 50161877Sumehandle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr) 502121474Sume{ 50355163Sshin GElf_Phdr phdr; 50455163Sshin uint32_t i; 50561877Sume char *core_cmdline; 50655163Sshin const char *seg_name; 50761877Sume 50861877Sume if (name == NULL || elf == NULL || elfhdr == NULL) 50955163Sshin return (RETURN_DATAERR); 51055163Sshin if (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE) 51155163Sshin return (RETURN_DATAERR); 51255163Sshin 51361877Sume seg_name = core_cmdline = NULL; 51455163Sshin if (style == STYLE_SYSV) 51555163Sshin sysv_header(name, NULL); 51655163Sshin else 51761877Sume berkeley_header(); 51861877Sume 51955163Sshin for (i = 0; i < elfhdr->e_phnum; i++) { 52055163Sshin if (gelf_getphdr(elf, i, &phdr) != NULL) { 52161877Sume if (phdr.p_type == PT_NOTE) { 52261877Sume handle_phdr(elf, elfhdr, &phdr, i, "note"); 523121474Sume handle_core_note(elf, elfhdr, &phdr, 524121474Sume &core_cmdline); 52555163Sshin } else { 52655163Sshin switch(phdr.p_type) { 527121474Sume case PT_NULL: 528121474Sume seg_name = "null"; 52955163Sshin break; 53061877Sume case PT_LOAD: 531121474Sume seg_name = "load"; 532121474Sume break; 533121474Sume case PT_DYNAMIC: 534121474Sume seg_name = "dynamic"; 535121474Sume break; 536121474Sume case PT_INTERP: 537121474Sume seg_name = "interp"; 53855163Sshin break; 539121474Sume case PT_SHLIB: 540121474Sume seg_name = "shlib"; 541121474Sume break; 542121474Sume case PT_PHDR: 543121474Sume seg_name = "phdr"; 54455163Sshin break; 545121474Sume case PT_GNU_EH_FRAME: 546121474Sume seg_name = "eh_frame_hdr"; 547121474Sume break; 548121474Sume case PT_GNU_STACK: 54955163Sshin seg_name = "stack"; 550121474Sume break; 551121474Sume default: 552121474Sume seg_name = "segment"; 553121474Sume } 554121474Sume handle_phdr(elf, elfhdr, &phdr, i, seg_name); 55555163Sshin } 55655163Sshin } 557121474Sume } 558121474Sume 559121474Sume if (style == STYLE_BERKELEY) { 560121474Sume if (core_cmdline != NULL) { 561121474Sume berkeley_footer(core_cmdline, name, 562121747Sume "core file invoked as"); 563121747Sume } else { 564121474Sume berkeley_footer(core_cmdline, name, "core file"); 565121747Sume } 566121474Sume } else { 567121425Sume sysv_footer(); 568121425Sume if (core_cmdline != NULL) { 56955163Sshin (void) printf(" (core file invoked as %s)\n\n", 57090053Sroam core_cmdline); 57155163Sshin } else { 572121474Sume (void) printf(" (core file)\n\n"); 573121474Sume } 574121474Sume } 57561877Sume free(core_cmdline); 57661877Sume return (RETURN_OK); 577121474Sume} 578121474Sume 57961877Sume/* 58061877Sume * Given an elf object,ar(1) filename, and based on the output style 58161877Sume * and radix format the various sections and their length will be printed 58255163Sshin * or the size of the text, data, bss sections will be printed out. 583121474Sume */ 584121474Sumestatic int 58561877Sumehandle_elf(char const *name) 58655163Sshin{ 587121474Sume GElf_Ehdr elfhdr; 588121474Sume GElf_Shdr shdr; 58961877Sume Elf *elf, *elf1; 590121474Sume Elf_Arhdr *arhdr; 591121474Sume Elf_Scn *scn; 592121474Sume Elf_Cmd elf_cmd; 59361877Sume int exit_code, fd; 594121474Sume 59555163Sshin if (name == NULL) 59661877Sume return (RETURN_NOINPUT); 59761877Sume 59861877Sume if ((fd = open(name, O_RDONLY, 0)) < 0) 59961877Sume return (RETURN_NOINPUT); 60061877Sume 601121474Sume elf_cmd = ELF_C_READ; 602121474Sume elf1 = elf_begin(fd, elf_cmd, NULL); 60361877Sume while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) { 60461877Sume arhdr = elf_getarhdr(elf); 60561877Sume if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) { 60655163Sshin (void) elf_end(elf); 60755163Sshin (void) elf_end(elf1); 608121747Sume (void) close(fd); 60961877Sume return (RETURN_DATAERR); 61061877Sume } 61161877Sume if (elf_kind(elf) != ELF_K_ELF || 612121747Sume (gelf_getehdr(elf, &elfhdr) == NULL)) { 613121747Sume elf_cmd = elf_next(elf); 614121747Sume (void) elf_end(elf); 615121747Sume warnx("%s: File format not recognized", 616121747Sume arhdr != NULL ? arhdr->ar_name : name); 617121747Sume continue; 61861877Sume } 61961877Sume /* Core dumps are handled separately */ 620121747Sume if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) { 621121747Sume exit_code = handle_core(name, elf, &elfhdr); 622121747Sume (void) elf_end(elf); 623121747Sume (void) elf_end(elf1); 624121747Sume (void) close(fd); 625121747Sume return (exit_code); 626121747Sume } else { 627121747Sume scn = NULL; 628121747Sume if (style == STYLE_BERKELEY) { 629121747Sume berkeley_header(); 63061877Sume while ((scn = elf_nextscn(elf, scn)) != NULL) { 631121474Sume if (gelf_getshdr(scn, &shdr) != NULL) 63261877Sume berkeley_calc(&shdr); 63361877Sume } 63455163Sshin } else { 635121747Sume sysv_header(name, arhdr); 636121747Sume scn = NULL; 637121474Sume while ((scn = elf_nextscn(elf, scn)) != NULL) { 638121474Sume if (gelf_getshdr(scn, &shdr) != NULL) 639121474Sume sysv_calc(elf, &elfhdr, &shdr); 64055163Sshin } 64155163Sshin } 64255163Sshin if (style == STYLE_BERKELEY) { 643121747Sume if (arhdr != NULL) { 644121747Sume berkeley_footer(name, arhdr->ar_name, 645121747Sume "ex"); 646121747Sume } else { 647121747Sume berkeley_footer(name, NULL, "ex"); 648121747Sume } 649121747Sume } else { 650121747Sume sysv_footer(); 651121747Sume } 652121747Sume } 653121747Sume elf_cmd = elf_next(elf); 654121747Sume (void) elf_end(elf); 655121747Sume } 656121747Sume (void) elf_end(elf1); 657121747Sume (void) close(fd); 658121747Sume return (RETURN_OK); 659121747Sume} 660121747Sume 661121747Sume/* 662121747Sume * Sysv formatting helper functions. 663121747Sume */ 664121747Sumestatic void 665121747Sumesysv_header(const char *name, Elf_Arhdr *arhdr) 666121747Sume{ 667121747Sume 668121747Sume text_size_total = 0; 669121747Sume if (arhdr != NULL) 670121747Sume (void) printf("%s (ex %s):\n", arhdr->ar_name, name); 671121747Sume else 672121747Sume (void) printf("%s :\n", name); 673121747Sume tbl_new(3); 674121747Sume tbl_append(); 675121747Sume tbl_print("section", 0); 676121747Sume tbl_print("size", 1); 677121747Sume tbl_print("addr", 2); 678121747Sume} 679121747Sume 680121747Sumestatic void 681121747Sumesysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr) 682121747Sume{ 683121747Sume char *section_name; 684121747Sume 685121747Sume section_name = elf_strptr(elf, elfhdr->e_shstrndx, 686121747Sume (size_t) shdr->sh_name); 687121747Sume if ((shdr->sh_type == SHT_SYMTAB || 688121747Sume shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA || 689121747Sume shdr->sh_type == SHT_REL) && shdr->sh_addr == 0) 690121747Sume return; 691121747Sume tbl_append(); 692121747Sume tbl_print(section_name, 0); 693121747Sume tbl_print_num(shdr->sh_size, radix, 1); 694121747Sume tbl_print_num(shdr->sh_addr, radix, 2); 695121747Sume text_size_total += shdr->sh_size; 696121747Sume} 697121747Sume 698121747Sumestatic void 699121747Sumesysv_footer(void) 700121747Sume{ 701121747Sume tbl_append(); 702121747Sume tbl_print("Total", 0); 703121747Sume tbl_print_num(text_size_total, radix, 1); 704121747Sume tbl_flush(); 705121747Sume putchar('\n'); 706121747Sume} 707121747Sume 708121747Sume/* 709121747Sume * berkeley style output formatting helper functions. 710121747Sume */ 711121747Sumestatic void 712121747Sumeberkeley_header(void) 713121747Sume{ 714121747Sume static int printed; 715121747Sume 716121747Sume text_size = data_size = bss_size = 0; 717121747Sume if (!printed) { 718121747Sume tbl_new(6); 719121747Sume tbl_append(); 720121747Sume tbl_print("text", 0); 721121747Sume tbl_print("data", 1); 722121747Sume tbl_print("bss", 2); 723121747Sume if (radix == RADIX_OCTAL) 724121747Sume tbl_print("oct", 3); 725121747Sume else 726121747Sume tbl_print("dec", 3); 727121747Sume tbl_print("hex", 4); 728121747Sume tbl_print("filename", 5); 729121747Sume printed = 1; 730121747Sume } 731121747Sume} 732121747Sume 733121747Sumestatic void 734121747Sumeberkeley_calc(GElf_Shdr *shdr) 735121747Sume{ 736121747Sume if (shdr != NULL) { 737121747Sume if (!(shdr->sh_flags & SHF_ALLOC)) 738121747Sume return; 739121747Sume if ((shdr->sh_flags & SHF_ALLOC) && 740121747Sume ((shdr->sh_flags & SHF_EXECINSTR) || 741121747Sume !(shdr->sh_flags & SHF_WRITE))) 742121747Sume text_size += shdr->sh_size; 743121747Sume else if ((shdr->sh_flags & SHF_ALLOC) && 744121747Sume (shdr->sh_flags & SHF_WRITE) && 745121747Sume (shdr->sh_type != SHT_NOBITS)) 746121747Sume data_size += shdr->sh_size; 747121747Sume else 748121747Sume bss_size += shdr->sh_size; 749121747Sume } 750121747Sume} 751121747Sume 752121747Sumestatic void 753121747Sumeberkeley_totals(void) 754121747Sume{ 755121747Sume uint64_t grand_total; 756121747Sume 757121747Sume grand_total = text_size_total + data_size_total + bss_size_total; 758121747Sume tbl_append(); 759121747Sume tbl_print_num(text_size_total, radix, 0); 760121747Sume tbl_print_num(data_size_total, radix, 1); 761121747Sume tbl_print_num(bss_size_total, radix, 2); 762121747Sume if (radix == RADIX_OCTAL) 763121747Sume tbl_print_num(grand_total, RADIX_OCTAL, 3); 764121747Sume else 765121747Sume tbl_print_num(grand_total, RADIX_DECIMAL, 3); 766121747Sume tbl_print_num(grand_total, RADIX_HEX, 4); 767121747Sume} 768121747Sume 769121747Sumestatic void 770121747Sumeberkeley_footer(const char *name, const char *ar_name, const char *msg) 771121747Sume{ 772121747Sume char buf[BUF_SIZE]; 773121747Sume 774121747Sume total_size = text_size + data_size + bss_size; 775121747Sume if (show_totals) { 776121747Sume text_size_total += text_size; 777121747Sume bss_size_total += bss_size; 778121747Sume data_size_total += data_size; 779121747Sume } 780121747Sume 781121747Sume tbl_append(); 782121747Sume tbl_print_num(text_size, radix, 0); 783121747Sume tbl_print_num(data_size, radix, 1); 784121747Sume tbl_print_num(bss_size, radix, 2); 785121747Sume if (radix == RADIX_OCTAL) 786121747Sume tbl_print_num(total_size, RADIX_OCTAL, 3); 787121747Sume else 788121747Sume tbl_print_num(total_size, RADIX_DECIMAL, 3); 789121747Sume tbl_print_num(total_size, RADIX_HEX, 4); 790121747Sume if (ar_name != NULL && name != NULL) 791121747Sume (void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg, 792121747Sume name); 793121747Sume else if (ar_name != NULL && name == NULL) 794121747Sume (void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg); 795121747Sume else 796121747Sume (void) snprintf(buf, BUF_SIZE, "%s", name); 797121747Sume tbl_print(buf, 5); 798121747Sume} 799121747Sume 800121747Sume 801121747Sumestatic void 802121747Sumetbl_new(int col) 803121747Sume{ 804121747Sume 805121747Sume assert(tb == NULL); 806121747Sume assert(col > 0); 807121747Sume if ((tb = calloc(1, sizeof(*tb))) == NULL) 808121747Sume err(EXIT_FAILURE, "calloc"); 809121747Sume if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL) 810121747Sume err(EXIT_FAILURE, "calloc"); 811121747Sume if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL) 812121747Sume err(EXIT_FAILURE, "calloc"); 813121747Sume tb->col = col; 814121747Sume tb->row = 0; 815121747Sume} 816121747Sume 817121747Sumestatic void 818121747Sumetbl_print(const char *s, int col) 819121747Sume{ 820121747Sume int len; 821121747Sume 822121747Sume assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col); 823121747Sume assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL); 824121747Sume if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL) 825121747Sume err(EXIT_FAILURE, "strdup"); 826121747Sume len = strlen(s); 827121747Sume if (len > tb->width[col]) 828121747Sume tb->width[col] = len; 829121747Sume} 830121747Sume 831121747Sumestatic void 832121747Sumetbl_print_num(uint64_t num, enum radix_style rad, int col) 833121747Sume{ 834121747Sume char buf[BUF_SIZE]; 835121747Sume 836121747Sume (void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" : 837121747Sume ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num); 838121747Sume tbl_print(buf, col); 839121747Sume} 840121747Sume 841121747Sumestatic void 842121747Sumetbl_append(void) 843121747Sume{ 844121747Sume int i; 845121747Sume 846121747Sume assert(tb != NULL && tb->col > 0); 847121747Sume tb->row++; 848121747Sume for (i = 0; i < tb->col; i++) { 849121747Sume tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row); 850121747Sume if (tb->tbl[i] == NULL) 851121747Sume err(EXIT_FAILURE, "realloc"); 852121747Sume tb->tbl[i][tb->row - 1] = NULL; 853121747Sume } 854121747Sume} 855121747Sume 856121747Sumestatic void 857121747Sumetbl_flush(void) 858121747Sume{ 859121747Sume const char *str; 860121747Sume int i, j; 861121747Sume 862121747Sume if (tb == NULL) 863121747Sume return; 864121747Sume 865121747Sume assert(tb->col > 0); 866121747Sume for (i = 0; i < tb->row; i++) { 867121747Sume if (style == STYLE_BERKELEY) 868121747Sume printf(" "); 869121747Sume for (j = 0; j < tb->col; j++) { 870121747Sume str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : ""); 871121747Sume if (style == STYLE_SYSV && j == 0) 872121747Sume printf("%-*s", tb->width[j], str); 873121747Sume else if (style == STYLE_BERKELEY && j == tb->col - 1) 874121747Sume printf("%s", str); 875121747Sume else 876121747Sume printf("%*s", tb->width[j], str); 877121747Sume if (j == tb->col -1) 878121747Sume putchar('\n'); 879121747Sume else 880121747Sume printf(" "); 881121747Sume } 882121747Sume } 883121747Sume 884121747Sume for (i = 0; i < tb->col; i++) { 885121747Sume for (j = 0; j < tb->row; j++) { 886121747Sume if (tb->tbl[i][j]) 887121747Sume free(tb->tbl[i][j]); 888121747Sume } 889121747Sume free(tb->tbl[i]); 890121747Sume } 891121747Sume free(tb->tbl); 892121747Sume free(tb->width); 893121747Sume free(tb); 894121747Sume tb = NULL; 895121747Sume} 896121747Sume 897121747Sume#define USAGE_MESSAGE "\ 898121747SumeUsage: %s [options] file ...\n\ 899121747Sume Display sizes of ELF sections.\n\n\ 900121747Sume Options:\n\ 901121747Sume --format=format Display output in specified format. Supported\n\ 902121747Sume values are `berkeley' and `sysv'.\n\ 903121747Sume --help Display this help message and exit.\n\ 904121747Sume --radix=radix Display numeric values in the specified radix.\n\ 905121747Sume Supported values are: 8, 10 and 16.\n\ 906121747Sume --totals Show cumulative totals of section sizes.\n\ 907121747Sume --version Display a version identifier and exit.\n\ 908121747Sume -A Equivalent to `--format=sysv'.\n\ 909121747Sume -B Equivalent to `--format=berkeley'.\n\ 910121747Sume -V Equivalent to `--version'.\n\ 911121747Sume -d Equivalent to `--radix=10'.\n\ 912121747Sume -h Same as option --help.\n\ 913121747Sume -o Equivalent to `--radix=8'.\n\ 914121747Sume -t Equivalent to option --totals.\n\ 915121747Sume -x Equivalent to `--radix=16'.\n" 916121747Sume 917121747Sumestatic void 918121747Sumeusage(void) 919121747Sume{ 920121747Sume (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); 921121747Sume exit(EXIT_FAILURE); 922121747Sume} 923121747Sume 924121747Sumestatic void 925121747Sumeshow_version(void) 926121747Sume{ 927121747Sume (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version()); 928121747Sume exit(EXIT_SUCCESS); 929121747Sume} 93055163Sshin