size.c revision 367466
1178172Simp/*- 2195162Simp * Copyright (c) 2007 S.Sam Arun Raj 3178172Simp * All rights reserved. 4178172Simp * 5178172Simp * Redistribution and use in source and binary forms, with or without 6178172Simp * modification, are permitted provided that the following conditions 7178172Simp * are met: 8178172Simp * 1. Redistributions of source code must retain the above copyright 9178172Simp * notice, this list of conditions and the following disclaimer. 10178172Simp * 2. Redistributions in binary form must reproduce the above copyright 11178172Simp * notice, this list of conditions and the following disclaimer in the 12178172Simp * documentation and/or other materials provided with the distribution. 13178172Simp * 14178172Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15178172Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16178172Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17178172Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18178172Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19178172Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20178172Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21178172Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22178172Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23178172Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24178172Simp * SUCH DAMAGE. 25178172Simp */ 26202046Simp 27178172Simp#include <assert.h> 28178172Simp#include <err.h> 29178172Simp#include <fcntl.h> 30178172Simp#include <gelf.h> 31178172Simp#include <getopt.h> 32202046Simp#include <libelftc.h> 33202046Simp#include <stdint.h> 34202046Simp#include <stdio.h> 35202046Simp#include <stdlib.h> 36178172Simp#include <string.h> 37178172Simp#include <unistd.h> 38178172Simp 39178172Simp#include "_elftc.h" 40178172Simp 41178172SimpELFTC_VCSID("$Id: size.c 3458 2016-05-09 15:01:25Z emaste $"); 42178172Simp 43178172Simp#define BUF_SIZE 1024 44178172Simp#define ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1)) 45178172Simp#define SIZE_VERSION_STRING "size 1.0" 46178172Simp 47178172Simpenum return_code { 48202046Simp RETURN_OK, 49178172Simp RETURN_NOINPUT, 50178172Simp RETURN_DATAERR, 51178172Simp RETURN_USAGE 52178172Simp}; 53178172Simp 54178172Simpenum output_style { 55178172Simp STYLE_BERKELEY, 56178172Simp STYLE_SYSV 57178172Simp}; 58204689Sneel 59202046Simpenum radix_style { 60178172Simp RADIX_OCTAL, 61202046Simp RADIX_DECIMAL, 62202046Simp RADIX_HEX 63202046Simp}; 64202046Simp 65202046Simpstatic uint64_t bss_size, data_size, text_size, total_size; 66202046Simpstatic uint64_t bss_size_total, data_size_total, text_size_total; 67178172Simpstatic int show_totals; 68178172Simpstatic int size_option; 69178172Simpstatic enum radix_style radix = RADIX_DECIMAL; 70232356Sjhbstatic enum output_style style = STYLE_BERKELEY; 71178172Simpstatic const char *default_args[2] = { "a.out", NULL }; 72178172Simp 73178172Simpstatic struct { 74178172Simp int row; 75178172Simp int col; 76178172Simp int *width; 77178172Simp char ***tbl; 78178172Simp} *tb; 79178172Simp 80178172Simpenum { 81178172Simp OPT_FORMAT, 82178172Simp OPT_RADIX 83240177Sjhb}; 84202046Simp 85178172Simpstatic struct option size_longopts[] = { 86178172Simp { "format", required_argument, &size_option, OPT_FORMAT }, 87202046Simp { "help", no_argument, NULL, 'h' }, 88202046Simp { "radix", required_argument, &size_option, OPT_RADIX }, 89202046Simp { "totals", no_argument, NULL, 't' }, 90202046Simp { "version", no_argument, NULL, 'V' }, 91202046Simp { NULL, 0, NULL, 0 } 92202046Simp}; 93202046Simp 94202046Simpstatic void berkeley_calc(GElf_Shdr *); 95202046Simpstatic void berkeley_footer(const char *, const char *, const char *); 96202046Simpstatic void berkeley_header(void); 97202046Simpstatic void berkeley_totals(void); 98202046Simpstatic int handle_core(char const *, Elf *elf, GElf_Ehdr *); 99202046Simpstatic void handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **); 100202046Simpstatic int handle_elf(char const *); 101202046Simpstatic void handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t, 102202046Simp const char *); 103202046Simpstatic void show_version(void); 104202046Simpstatic void sysv_header(const char *, Elf_Arhdr *); 105202046Simpstatic void sysv_footer(void); 106202046Simpstatic void sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *); 107202046Simpstatic void usage(void); 108202046Simpstatic void tbl_new(int); 109202046Simpstatic void tbl_print(const char *, int); 110202046Simpstatic void tbl_print_num(uint64_t, enum radix_style, int); 111202046Simpstatic void tbl_append(void); 112202046Simpstatic void tbl_flush(void); 113202046Simp 114202046Simp/* 115202046Simp * size utility using elf(3) and gelf(3) API to list section sizes and 116202046Simp * total in elf files. Supports only elf files (core dumps in elf 117202046Simp * included) that can be opened by libelf, other formats are not supported. 118202046Simp */ 119202046Simpint 120202046Simpmain(int argc, char **argv) 121227309Sed{ 122202046Simp int ch, r, rc; 123202046Simp const char **files, *fn; 124202046Simp 125178172Simp rc = RETURN_OK; 126178172Simp 127178172Simp if (elf_version(EV_CURRENT) == EV_NONE) 128178172Simp errx(EXIT_FAILURE, "ELF library initialization failed: %s", 129204689Sneel elf_errmsg(-1)); 130202046Simp 131202046Simp while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts, 132202046Simp NULL)) != -1) 133178172Simp switch((char)ch) { 134202046Simp case 'A': 135202046Simp style = STYLE_SYSV; 136202046Simp break; 137212284Sjchandra case 'B': 138178172Simp style = STYLE_BERKELEY; 139178172Simp break; 140178172Simp case 'V': 141178172Simp show_version(); 142178172Simp break; 143178172Simp case 'd': 144202046Simp radix = RADIX_DECIMAL; 145202046Simp break; 146212284Sjchandra case 'o': 147202046Simp radix = RADIX_OCTAL; 148178172Simp break; 149178172Simp case 't': 150202046Simp show_totals = 1; 151202046Simp break; 152202046Simp case 'x': 153178172Simp radix = RADIX_HEX; 154178172Simp break; 155178172Simp case 0: 156178172Simp switch (size_option) { 157178172Simp case OPT_FORMAT: 158178172Simp if (*optarg == 's' || *optarg == 'S') 159178172Simp style = STYLE_SYSV; 160178172Simp else if (*optarg == 'b' || *optarg == 'B') 161178172Simp style = STYLE_BERKELEY; 162178172Simp else { 163202046Simp warnx("unrecognized format \"%s\".", 164202046Simp optarg); 165202046Simp usage(); 166202046Simp } 167202046Simp break; 168202046Simp case OPT_RADIX: 169202046Simp r = strtol(optarg, NULL, 10); 170202046Simp if (r == 8) 171202046Simp radix = RADIX_OCTAL; 172202046Simp else if (r == 10) 173202046Simp radix = RADIX_DECIMAL; 174202046Simp else if (r == 16) 175202046Simp radix = RADIX_HEX; 176202046Simp else { 177202046Simp warnx("unsupported radix \"%s\".", 178202046Simp optarg); 179202046Simp usage(); 180202046Simp } 181202046Simp break; 182202046Simp default: 183202046Simp err(EXIT_FAILURE, "Error in option handling."); 184202046Simp /*NOTREACHED*/ 185202046Simp } 186202046Simp break; 187202046Simp case 'h': 188202046Simp case '?': 189202046Simp default: 190202046Simp usage(); 191202046Simp /* NOTREACHED */ 192202046Simp } 193202046Simp argc -= optind; 194202046Simp argv += optind; 195202046Simp 196202046Simp files = (argc == 0) ? default_args : (void *) argv; 197202046Simp 198202046Simp while ((fn = *files) != NULL) { 199202046Simp rc = handle_elf(fn); 200202046Simp if (rc != RETURN_OK) 201202046Simp warnx(rc == RETURN_NOINPUT ? 202178172Simp "'%s': No such file" : 203178172Simp "%s: File format not recognized", fn); 204178172Simp files++; 205178172Simp } 206178172Simp if (style == STYLE_BERKELEY) { 207178172Simp if (show_totals) 208178172Simp berkeley_totals(); 209178172Simp tbl_flush(); 210178172Simp } 211178172Simp return (rc); 212178172Simp} 213178172Simp 214178172Simpstatic int 215178172Simpxlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst, 216178172Simp Elf_Type type, size_t size) 217178172Simp{ 218178172Simp Elf_Data src, dst; 219178172Simp 220178172Simp src.d_buf = _src; 221178172Simp src.d_type = type; 222202046Simp src.d_version = elfhdr->e_version; 223202046Simp src.d_size = size; 224202046Simp dst.d_buf = _dst; 225202046Simp dst.d_version = elfhdr->e_version; 226202046Simp dst.d_size = size; 227202046Simp return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA]) != 228202046Simp NULL ? 0 : 1); 229202046Simp} 230202046Simp 231202046Simp#define NOTE_OFFSET_32(nhdr, namesz, offset) \ 232202046Simp ((char *)nhdr + sizeof(Elf32_Nhdr) + \ 233202046Simp ELF_ALIGN((int32_t)namesz, 4) + offset) 234202046Simp 235178172Simp#define NOTE_OFFSET_64(nhdr, namesz, offset) \ 236178172Simp ((char *)nhdr + sizeof(Elf32_Nhdr) + \ 237178172Simp ELF_ALIGN((int32_t)namesz, 8) + offset) 238178172Simp 239178172Simp#define PID32(nhdr, namesz, offset) \ 240178172Simp (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr, \ 241178172Simp namesz, offset))); 242178172Simp 243178172Simp#define PID64(nhdr, namesz, offset) \ 244178172Simp (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr, \ 245178172Simp namesz, offset))); 246178172Simp 247178172Simp#define NEXT_NOTE(elfhdr, descsz, namesz, offset) do { \ 248178172Simp if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { \ 249178172Simp offset += ELF_ALIGN((int32_t)descsz, 4) + \ 250178172Simp sizeof(Elf32_Nhdr) + \ 251178172Simp ELF_ALIGN((int32_t)namesz, 4); \ 252178172Simp } else { \ 253178172Simp offset += ELF_ALIGN((int32_t)descsz, 8) + \ 254178172Simp sizeof(Elf32_Nhdr) + \ 255178172Simp ELF_ALIGN((int32_t)namesz, 8); \ 256178172Simp } \ 257178172Simp} while (0) 258178172Simp 259178172Simp/* 260178172Simp * Parse individual note entries inside a PT_NOTE segment. 261178172Simp */ 262178172Simpstatic void 263178172Simphandle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, 264178172Simp char **cmd_line) 265178172Simp{ 266178172Simp size_t max_size, segment_end; 267178172Simp uint64_t raw_size; 268178172Simp GElf_Off offset; 269178172Simp static pid_t pid; 270178172Simp uintptr_t ver; 271178172Simp Elf32_Nhdr *nhdr, nhdr_l; 272178172Simp static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/; 273178172Simp char buf[BUF_SIZE], *data, *name; 274178172Simp 275178172Simp if (elf == NULL || elfhdr == NULL || phdr == NULL) 276178172Simp return; 277178172Simp 278178172Simp data = elf_rawfile(elf, &max_size); 279178172Simp offset = phdr->p_offset; 280178172Simp if (offset >= max_size || phdr->p_filesz > max_size - offset) { 281178172Simp warnx("invalid PHDR offset"); 282178172Simp return; 283178172Simp } 284178172Simp segment_end = phdr->p_offset + phdr->p_filesz; 285178172Simp 286178172Simp while (data != NULL && offset + sizeof(Elf32_Nhdr) < segment_end) { 287178172Simp nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset); 288178172Simp memset(&nhdr_l, 0, sizeof(Elf32_Nhdr)); 289178172Simp if (xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type, 290178172Simp ELF_T_WORD, sizeof(Elf32_Word)) != 0 || 291202046Simp xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz, 292178172Simp ELF_T_WORD, sizeof(Elf32_Word)) != 0 || 293178172Simp xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz, 294178172Simp ELF_T_WORD, sizeof(Elf32_Word)) != 0) 295178172Simp break; 296178172Simp 297178172Simp if (offset + sizeof(Elf32_Nhdr) + 298178172Simp ELF_ALIGN(nhdr_l.n_namesz, 4) + 299178172Simp ELF_ALIGN(nhdr_l.n_descsz, 4) >= segment_end) { 300178172Simp warnx("invalid note header"); 301178172Simp return; 302178172Simp } 303178172Simp 304178172Simp name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr)); 305178172Simp switch (nhdr_l.n_type) { 306178172Simp case NT_PRSTATUS: { 307202046Simp raw_size = 0; 308202046Simp if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD && 309202046Simp nhdr_l.n_namesz == 0x8 && 310202046Simp !strcmp(name,"FreeBSD")) { 311202046Simp if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { 312178172Simp raw_size = (uint64_t)*((uint32_t *) 313178172Simp (uintptr_t)(name + 314232356Sjhb ELF_ALIGN((int32_t) 315212284Sjchandra nhdr_l.n_namesz, 4) + 8)); 316212284Sjchandra ver = (uintptr_t)NOTE_OFFSET_32(nhdr, 317212284Sjchandra nhdr_l.n_namesz,0); 318212284Sjchandra if (*((int *)ver) == 1) 319178172Simp pid = PID32(nhdr, 320178172Simp nhdr_l.n_namesz, 24); 321178172Simp } else { 322178172Simp raw_size = *((uint64_t *)(uintptr_t) 323178172Simp (name + ELF_ALIGN((int32_t) 324202046Simp nhdr_l.n_namesz, 8) + 16)); 325202046Simp ver = (uintptr_t)NOTE_OFFSET_64(nhdr, 326178172Simp nhdr_l.n_namesz,0); 327202046Simp if (*((int *)ver) == 1) 328178172Simp pid = PID64(nhdr, 329178172Simp nhdr_l.n_namesz, 40); 330178172Simp } 331178172Simp (void)xlatetom(elf, elfhdr, &raw_size, 332178172Simp &raw_size, ELF_T_WORD, sizeof(uint64_t)); 333178172Simp (void)xlatetom(elf, elfhdr, &pid, &pid, 334178172Simp ELF_T_WORD, sizeof(pid_t)); 335178172Simp } 336178172Simp 337202046Simp if (raw_size != 0 && style == STYLE_SYSV) { 338202046Simp (void) snprintf(buf, BUF_SIZE, "%s/%d", 339178172Simp ".reg", pid); 340178172Simp tbl_append(); 341212284Sjchandra tbl_print(buf, 0); 342212284Sjchandra tbl_print_num(raw_size, radix, 1); 343178172Simp tbl_print_num(0, radix, 2); 344178172Simp if (!reg_pseudo) { 345204689Sneel tbl_append(); 346204689Sneel tbl_print(".reg", 0); 347178172Simp tbl_print_num(raw_size, radix, 1); 348178172Simp tbl_print_num(0, radix, 2); 349178172Simp reg_pseudo = 1; 350178172Simp text_size_total += raw_size; 351178172Simp } 352178172Simp text_size_total += raw_size; 353178172Simp } 354178172Simp } 355178172Simp break; 356240177Sjhb case NT_FPREGSET: /* same as NT_PRFPREG */ 357240177Sjhb if (style == STYLE_SYSV) { 358212284Sjchandra (void) snprintf(buf, BUF_SIZE, 359202046Simp "%s/%d", ".reg2", pid); 360202046Simp tbl_append(); 361212284Sjchandra tbl_print(buf, 0); 362232356Sjhb tbl_print_num(nhdr_l.n_descsz, radix, 1); 363232356Sjhb tbl_print_num(0, radix, 2); 364178172Simp if (!reg2_pseudo) { 365178172Simp tbl_append(); 366178172Simp tbl_print(".reg2", 0); 367212284Sjchandra tbl_print_num(nhdr_l.n_descsz, radix, 368232356Sjhb 1); 369202046Simp tbl_print_num(0, radix, 2); 370202046Simp reg2_pseudo = 1; 371202046Simp text_size_total += nhdr_l.n_descsz; 372212284Sjchandra } 373212284Sjchandra text_size_total += nhdr_l.n_descsz; 374212284Sjchandra } 375212284Sjchandra break; 376212284Sjchandra#if 0 377212284Sjchandra case NT_AUXV: 378212284Sjchandra if (style == STYLE_SYSV) { 379212284Sjchandra tbl_append(); 380178172Simp tbl_print(".auxv", 0); 381178172Simp tbl_print_num(nhdr_l.n_descsz, radix, 1); 382178172Simp tbl_print_num(0, radix, 2); 383178172Simp text_size_total += nhdr_l.n_descsz; 384202046Simp } 385202046Simp break; 386202046Simp case NT_PRXFPREG: 387178172Simp if (style == STYLE_SYSV) { 388202046Simp (void) snprintf(buf, BUF_SIZE, "%s/%d", 389202046Simp ".reg-xfp", pid); 390202046Simp tbl_append(); 391202046Simp tbl_print(buf, 0); 392202046Simp tbl_print_num(nhdr_l.n_descsz, radix, 1); 393202046Simp tbl_print_num(0, radix, 2); 394202046Simp if (!regxfp_pseudo) { 395202046Simp tbl_append(); 396202046Simp tbl_print(".reg-xfp", 0); 397202046Simp tbl_print_num(nhdr_l.n_descsz, radix, 398202046Simp 1); 399202046Simp tbl_print_num(0, radix, 2); 400202046Simp regxfp_pseudo = 1; 401202046Simp text_size_total += nhdr_l.n_descsz; 402202046Simp } 403202046Simp text_size_total += nhdr_l.n_descsz; 404202046Simp } 405202046Simp break; 406202046Simp case NT_PSINFO: 407202046Simp#endif 408202046Simp case NT_PRPSINFO: { 409202046Simp /* FreeBSD 64-bit */ 410202046Simp if (nhdr_l.n_descsz == 0x78 && 411202046Simp !strcmp(name,"FreeBSD")) { 412202046Simp *cmd_line = strdup(NOTE_OFFSET_64(nhdr, 413202046Simp nhdr_l.n_namesz, 33)); 414178172Simp /* FreeBSD 32-bit */ 415202046Simp } else if (nhdr_l.n_descsz == 0x6c && 416178172Simp !strcmp(name,"FreeBSD")) { 417178172Simp *cmd_line = strdup(NOTE_OFFSET_32(nhdr, 418178172Simp nhdr_l.n_namesz, 25)); 419202046Simp } 420178172Simp /* Strip any trailing spaces */ 421178172Simp if (*cmd_line != NULL) { 422178172Simp char *s; 423178172Simp 424178172Simp s = *cmd_line + strlen(*cmd_line); 425178172Simp while (s > *cmd_line) { 426178172Simp if (*(s-1) != 0x20) break; 427178172Simp s--; 428178172Simp } 429178172Simp *s = 0; 430178172Simp } 431212284Sjchandra break; 432212284Sjchandra } 433178172Simp#if 0 434212284Sjchandra case NT_PSTATUS: 435212284Sjchandra case NT_LWPSTATUS: 436178172Simp#endif 437212284Sjchandra default: 438212284Sjchandra break; 439212284Sjchandra } 440240177Sjhb NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset); 441240177Sjhb } 442212284Sjchandra} 443212284Sjchandra 444212284Sjchandra/* 445212284Sjchandra * Handles program headers except for PT_NOTE, when sysv output style is 446212284Sjchandra * chosen, prints out the segment name and length. For berkely output 447212284Sjchandra * style only PT_LOAD segments are handled, and text, 448212284Sjchandra * data, bss size is calculated for them. 449212284Sjchandra */ 450240177Sjhbstatic void 451212284Sjchandrahandle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, 452212284Sjchandra uint32_t idx, const char *name) 453178172Simp{ 454178172Simp uint64_t addr, size; 455212284Sjchandra int split; 456178172Simp char buf[BUF_SIZE]; 457178172Simp 458202046Simp if (elf == NULL || elfhdr == NULL || phdr == NULL) 459178172Simp return; 460178172Simp 461178172Simp split = (phdr->p_memsz > 0) && (phdr->p_filesz > 0) && 462178172Simp (phdr->p_memsz > phdr->p_filesz); 463178172Simp 464178172Simp if (style == STYLE_SYSV) { 465178172Simp (void) snprintf(buf, BUF_SIZE, 466178172Simp "%s%d%s", name, idx, (split ? "a" : "")); 467178172Simp tbl_append(); 468178172Simp tbl_print(buf, 0); 469240177Sjhb tbl_print_num(phdr->p_filesz, radix, 1); 470240177Sjhb tbl_print_num(phdr->p_vaddr, radix, 2); 471240177Sjhb text_size_total += phdr->p_filesz; 472240177Sjhb if (split) { 473240177Sjhb size = phdr->p_memsz - phdr->p_filesz; 474240177Sjhb addr = phdr->p_vaddr + phdr->p_filesz; 475240177Sjhb (void) snprintf(buf, BUF_SIZE, "%s%d%s", name, 476240177Sjhb idx, "b"); 477240177Sjhb text_size_total += phdr->p_memsz - phdr->p_filesz; 478240177Sjhb tbl_append(); 479240177Sjhb tbl_print(buf, 0); 480178172Simp tbl_print_num(size, radix, 1); 481178172Simp tbl_print_num(addr, radix, 2); 482178172Simp } 483178172Simp } else { 484178172Simp if (phdr->p_type != PT_LOAD) 485178172Simp return; 486178172Simp if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) { 487202046Simp data_size += phdr->p_filesz; 488178172Simp if (split) 489178172Simp data_size += phdr->p_memsz - phdr->p_filesz; 490202046Simp } else { 491202046Simp text_size += phdr->p_filesz; 492202046Simp if (split) 493202046Simp text_size += phdr->p_memsz - phdr->p_filesz; 494202046Simp } 495202046Simp } 496202046Simp} 497202046Simp 498202046Simp/* 499202046Simp * Given a core dump file, this function maps program headers to segments. 500202046Simp */ 501202046Simpstatic int 502202046Simphandle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr) 503202046Simp{ 504202046Simp GElf_Phdr phdr; 505202046Simp uint32_t i; 506202046Simp char *core_cmdline; 507202046Simp const char *seg_name; 508202046Simp 509202046Simp if (name == NULL || elf == NULL || elfhdr == NULL) 510202046Simp return (RETURN_DATAERR); 511202046Simp if (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE) 512202046Simp return (RETURN_DATAERR); 513202046Simp 514202046Simp seg_name = core_cmdline = NULL; 515202046Simp if (style == STYLE_SYSV) 516202046Simp sysv_header(name, NULL); 517202046Simp else 518202046Simp berkeley_header(); 519202046Simp 520202046Simp for (i = 0; i < elfhdr->e_phnum; i++) { 521202046Simp if (gelf_getphdr(elf, i, &phdr) != NULL) { 522202046Simp if (phdr.p_type == PT_NOTE) { 523202046Simp handle_phdr(elf, elfhdr, &phdr, i, "note"); 524202046Simp handle_core_note(elf, elfhdr, &phdr, 525202046Simp &core_cmdline); 526202046Simp } else { 527202046Simp switch(phdr.p_type) { 528202046Simp case PT_NULL: 529202046Simp seg_name = "null"; 530202046Simp break; 531202046Simp case PT_LOAD: 532202046Simp seg_name = "load"; 533202046Simp break; 534202046Simp case PT_DYNAMIC: 535202046Simp seg_name = "dynamic"; 536202046Simp break; 537202046Simp case PT_INTERP: 538178172Simp seg_name = "interp"; 539178172Simp break; 540178172Simp case PT_SHLIB: 541178172Simp seg_name = "shlib"; 542178172Simp break; 543178172Simp case PT_PHDR: 544178172Simp seg_name = "phdr"; 545178172Simp break; 546178172Simp case PT_GNU_EH_FRAME: 547178172Simp seg_name = "eh_frame_hdr"; 548178172Simp break; 549178172Simp case PT_GNU_STACK: 550178172Simp seg_name = "stack"; 551202046Simp break; 552202046Simp default: 553202046Simp seg_name = "segment"; 554202046Simp } 555202046Simp handle_phdr(elf, elfhdr, &phdr, i, seg_name); 556202046Simp } 557202046Simp } 558202046Simp } 559178172Simp 560242465Sadrian if (style == STYLE_BERKELEY) { 561178172Simp if (core_cmdline != NULL) { 562178172Simp berkeley_footer(core_cmdline, name, 563178172Simp "core file invoked as"); 564178172Simp } else { 565178172Simp berkeley_footer(core_cmdline, name, "core file"); 566178172Simp } 567178172Simp } else { 568178172Simp sysv_footer(); 569178172Simp if (core_cmdline != NULL) { 570178172Simp (void) printf(" (core file invoked as %s)\n\n", 571178172Simp core_cmdline); 572212284Sjchandra } else { 573178172Simp (void) printf(" (core file)\n\n"); 574178172Simp } 575178172Simp } 576178172Simp free(core_cmdline); 577178172Simp return (RETURN_OK); 578178172Simp} 579178172Simp 580178172Simp/* 581178172Simp * Given an elf object,ar(1) filename, and based on the output style 582240177Sjhb * and radix format the various sections and their length will be printed 583240177Sjhb * or the size of the text, data, bss sections will be printed out. 584240177Sjhb */ 585240177Sjhbstatic int 586240177Sjhbhandle_elf(char const *name) 587240177Sjhb{ 588240177Sjhb GElf_Ehdr elfhdr; 589240177Sjhb GElf_Shdr shdr; 590240177Sjhb Elf *elf, *elf1; 591240177Sjhb Elf_Arhdr *arhdr; 592178172Simp Elf_Scn *scn; 593178172Simp Elf_Cmd elf_cmd; 594178172Simp int exit_code, fd; 595178172Simp 596178172Simp if (name == NULL) 597178172Simp return (RETURN_NOINPUT); 598178172Simp 599178172Simp if ((fd = open(name, O_RDONLY, 0)) < 0) 600178172Simp return (RETURN_NOINPUT); 601178172Simp 602178172Simp elf_cmd = ELF_C_READ; 603178172Simp elf1 = elf_begin(fd, elf_cmd, NULL); 604202046Simp while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) { 605204689Sneel arhdr = elf_getarhdr(elf); 606204689Sneel if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) { 607204689Sneel (void) elf_end(elf); 608204689Sneel (void) elf_end(elf1); 609204689Sneel (void) close(fd); 610204689Sneel return (RETURN_DATAERR); 611204689Sneel } 612204689Sneel if (elf_kind(elf) != ELF_K_ELF || 613204689Sneel (gelf_getehdr(elf, &elfhdr) == NULL)) { 614204689Sneel elf_cmd = elf_next(elf); 615202046Simp (void) elf_end(elf); 616204689Sneel warnx("%s: File format not recognized", 617204689Sneel arhdr != NULL ? arhdr->ar_name : name); 618212284Sjchandra continue; 619202046Simp } 620202046Simp /* Core dumps are handled separately */ 621204689Sneel if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) { 622178172Simp exit_code = handle_core(name, elf, &elfhdr); 623202046Simp (void) elf_end(elf); 624212284Sjchandra (void) elf_end(elf1); 625212284Sjchandra (void) close(fd); 626212284Sjchandra return (exit_code); 627212284Sjchandra } else { 628212284Sjchandra scn = NULL; 629212284Sjchandra if (style == STYLE_BERKELEY) { 630212284Sjchandra berkeley_header(); 631212284Sjchandra while ((scn = elf_nextscn(elf, scn)) != NULL) { 632212284Sjchandra if (gelf_getshdr(scn, &shdr) != NULL) 633212284Sjchandra berkeley_calc(&shdr); 634212284Sjchandra } 635178172Simp } else { 636178172Simp sysv_header(name, arhdr); 637178172Simp scn = NULL; 638178172Simp while ((scn = elf_nextscn(elf, scn)) != NULL) { 639178172Simp if (gelf_getshdr(scn, &shdr) != NULL) 640212284Sjchandra sysv_calc(elf, &elfhdr, &shdr); 641178172Simp } 642202046Simp } 643204689Sneel if (style == STYLE_BERKELEY) { 644178172Simp if (arhdr != NULL) { 645178172Simp berkeley_footer(name, arhdr->ar_name, 646178172Simp "ex"); 647212283Sjchandra } else { 648212283Sjchandra berkeley_footer(name, NULL, "ex"); 649178172Simp } 650178172Simp } else { 651178172Simp sysv_footer(); 652178172Simp } 653178172Simp } 654178172Simp elf_cmd = elf_next(elf); 655178172Simp (void) elf_end(elf); 656202046Simp } 657178172Simp (void) elf_end(elf1); 658202046Simp (void) close(fd); 659212284Sjchandra return (RETURN_OK); 660178172Simp} 661178172Simp 662178172Simp/* 663178172Simp * Sysv formatting helper functions. 664178172Simp */ 665178172Simpstatic void 666178172Simpsysv_header(const char *name, Elf_Arhdr *arhdr) 667178172Simp{ 668178172Simp 669178172Simp text_size_total = 0; 670178172Simp if (arhdr != NULL) 671178172Simp (void) printf("%s (ex %s):\n", arhdr->ar_name, name); 672178172Simp else 673178172Simp (void) printf("%s :\n", name); 674202046Simp tbl_new(3); 675212283Sjchandra tbl_append(); 676212283Sjchandra tbl_print("section", 0); 677212284Sjchandra tbl_print("size", 1); 678178172Simp tbl_print("addr", 2); 679212284Sjchandra} 680178172Simp 681202046Simpstatic void 682178172Simpsysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr) 683178172Simp{ 684178172Simp char *section_name; 685202046Simp 686178172Simp section_name = elf_strptr(elf, elfhdr->e_shstrndx, 687202046Simp (size_t) shdr->sh_name); 688202046Simp if ((shdr->sh_type == SHT_SYMTAB || 689202046Simp shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA || 690202046Simp shdr->sh_type == SHT_REL) && shdr->sh_addr == 0) 691202046Simp return; 692202046Simp tbl_append(); 693202046Simp tbl_print(section_name, 0); 694202046Simp tbl_print_num(shdr->sh_size, radix, 1); 695202046Simp tbl_print_num(shdr->sh_addr, radix, 2); 696202046Simp text_size_total += shdr->sh_size; 697202046Simp} 698202046Simp 699202046Simpstatic void 700202046Simpsysv_footer(void) 701202046Simp{ 702202046Simp tbl_append(); 703202046Simp tbl_print("Total", 0); 704206405Snwhitehorn tbl_print_num(text_size_total, radix, 1); 705202046Simp tbl_flush(); 706202046Simp putchar('\n'); 707202046Simp} 708206405Snwhitehorn 709206405Snwhitehorn/* 710202046Simp * berkeley style output formatting helper functions. 711206405Snwhitehorn */ 712202046Simpstatic void 713202046Simpberkeley_header(void) 714206405Snwhitehorn{ 715206405Snwhitehorn static int printed; 716202046Simp 717206405Snwhitehorn text_size = data_size = bss_size = 0; 718206405Snwhitehorn if (!printed) { 719202046Simp tbl_new(6); 720202046Simp tbl_append(); 721202046Simp tbl_print("text", 0); 722202046Simp tbl_print("data", 1); 723202046Simp tbl_print("bss", 2); 724202046Simp if (radix == RADIX_OCTAL) 725202046Simp tbl_print("oct", 3); 726202046Simp else 727202046Simp tbl_print("dec", 3); 728202046Simp tbl_print("hex", 4); 729202046Simp tbl_print("filename", 5); 730202046Simp printed = 1; 731202046Simp } 732202046Simp} 733202046Simp 734202046Simpstatic void 735202046Simpberkeley_calc(GElf_Shdr *shdr) 736202046Simp{ 737202046Simp if (shdr != NULL) { 738202046Simp if (!(shdr->sh_flags & SHF_ALLOC)) 739202046Simp return; 740202046Simp if ((shdr->sh_flags & SHF_ALLOC) && 741202046Simp ((shdr->sh_flags & SHF_EXECINSTR) || 742202046Simp !(shdr->sh_flags & SHF_WRITE))) 743202046Simp text_size += shdr->sh_size; 744178172Simp else if ((shdr->sh_flags & SHF_ALLOC) && 745178172Simp (shdr->sh_flags & SHF_WRITE) && 746178172Simp (shdr->sh_type != SHT_NOBITS)) 747178172Simp data_size += shdr->sh_size; 748178172Simp else 749178172Simp bss_size += shdr->sh_size; 750178172Simp } 751178172Simp} 752178172Simp 753178172Simpstatic void 754178172Simpberkeley_totals(void) 755178172Simp{ 756178172Simp uint64_t grand_total; 757178172Simp 758202046Simp grand_total = text_size_total + data_size_total + bss_size_total; 759178172Simp tbl_append(); 760178172Simp tbl_print_num(text_size_total, radix, 0); 761178172Simp tbl_print_num(data_size_total, radix, 1); 762178172Simp tbl_print_num(bss_size_total, radix, 2); 763178172Simp if (radix == RADIX_OCTAL) 764178172Simp tbl_print_num(grand_total, RADIX_OCTAL, 3); 765178172Simp else 766202046Simp tbl_print_num(grand_total, RADIX_DECIMAL, 3); 767202046Simp tbl_print_num(grand_total, RADIX_HEX, 4); 768202046Simp} 769202046Simp 770202046Simpstatic void 771202046Simpberkeley_footer(const char *name, const char *ar_name, const char *msg) 772202046Simp{ 773202046Simp char buf[BUF_SIZE]; 774202046Simp 775178172Simp total_size = text_size + data_size + bss_size; 776178172Simp if (show_totals) { 777178172Simp text_size_total += text_size; 778202046Simp bss_size_total += bss_size; 779202046Simp data_size_total += data_size; 780202046Simp } 781178172Simp 782178172Simp tbl_append(); 783178172Simp tbl_print_num(text_size, radix, 0); 784178172Simp tbl_print_num(data_size, radix, 1); 785178172Simp tbl_print_num(bss_size, radix, 2); 786178172Simp if (radix == RADIX_OCTAL) 787178172Simp tbl_print_num(total_size, RADIX_OCTAL, 3); 788178172Simp else 789202046Simp tbl_print_num(total_size, RADIX_DECIMAL, 3); 790202046Simp tbl_print_num(total_size, RADIX_HEX, 4); 791178172Simp if (ar_name != NULL && name != NULL) 792178172Simp (void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg, 793178172Simp name); 794178172Simp else if (ar_name != NULL && name == NULL) 795202046Simp (void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg); 796202046Simp else 797202046Simp (void) snprintf(buf, BUF_SIZE, "%s", name); 798202046Simp tbl_print(buf, 5); 799202046Simp} 800202046Simp 801202046Simp 802202046Simpstatic void 803202046Simptbl_new(int col) 804202046Simp{ 805202046Simp 806202046Simp assert(tb == NULL); 807202046Simp assert(col > 0); 808178172Simp if ((tb = calloc(1, sizeof(*tb))) == NULL) 809178172Simp err(EXIT_FAILURE, "calloc"); 810178172Simp if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL) 811178172Simp err(EXIT_FAILURE, "calloc"); 812178172Simp if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL) 813178172Simp err(EXIT_FAILURE, "calloc"); 814178172Simp tb->col = col; 815178172Simp tb->row = 0; 816178172Simp} 817178172Simp 818178172Simpstatic void 819178172Simptbl_print(const char *s, int col) 820178172Simp{ 821178172Simp int len; 822178172Simp 823178172Simp assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col); 824178172Simp assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL); 825178172Simp if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL) 826178172Simp err(EXIT_FAILURE, "strdup"); 827178172Simp len = strlen(s); 828178172Simp if (len > tb->width[col]) 829178172Simp tb->width[col] = len; 830178172Simp} 831178172Simp 832178172Simpstatic void 833178172Simptbl_print_num(uint64_t num, enum radix_style rad, int col) 834178172Simp{ 835178172Simp char buf[BUF_SIZE]; 836178172Simp 837178172Simp (void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" : 838178172Simp ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num); 839202046Simp tbl_print(buf, col); 840202046Simp} 841178172Simp 842178172Simpstatic void 843178172Simptbl_append(void) 844178172Simp{ 845178172Simp int i; 846178172Simp 847178172Simp assert(tb != NULL && tb->col > 0); 848212284Sjchandra tb->row++; 849212284Sjchandra for (i = 0; i < tb->col; i++) { 850178172Simp tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row); 851212284Sjchandra if (tb->tbl[i] == NULL) 852178172Simp err(EXIT_FAILURE, "realloc"); 853178172Simp tb->tbl[i][tb->row - 1] = NULL; 854178172Simp } 855178172Simp} 856202046Simp 857202046Simpstatic void 858178172Simptbl_flush(void) 859202046Simp{ 860178172Simp const char *str; 861178172Simp int i, j; 862178172Simp 863240177Sjhb if (tb == NULL) 864178172Simp return; 865202046Simp 866202046Simp assert(tb->col > 0); 867178172Simp for (i = 0; i < tb->row; i++) { 868178172Simp if (style == STYLE_BERKELEY) 869178172Simp printf(" "); 870240177Sjhb for (j = 0; j < tb->col; j++) { 871178172Simp str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : ""); 872178172Simp if (style == STYLE_SYSV && j == 0) 873178172Simp printf("%-*s", tb->width[j], str); 874178172Simp else if (style == STYLE_BERKELEY && j == tb->col - 1) 875202046Simp printf("%s", str); 876178172Simp else 877178172Simp printf("%*s", tb->width[j], str); 878178172Simp if (j == tb->col -1) 879178172Simp putchar('\n'); 880178172Simp else 881178172Simp printf(" "); 882178172Simp } 883212284Sjchandra } 884212284Sjchandra 885178172Simp for (i = 0; i < tb->col; i++) { 886178172Simp for (j = 0; j < tb->row; j++) { 887178172Simp if (tb->tbl[i][j]) 888178172Simp free(tb->tbl[i][j]); 889178172Simp } 890178172Simp free(tb->tbl[i]); 891202046Simp } 892178172Simp free(tb->tbl); 893178172Simp free(tb->width); 894178172Simp free(tb); 895178172Simp tb = NULL; 896178172Simp} 897178172Simp 898178172Simp#define USAGE_MESSAGE "\ 899178172SimpUsage: %s [options] file ...\n\ 900178172Simp Display sizes of ELF sections.\n\n\ 901240177Sjhb Options:\n\ 902188506Simp --format=format Display output in specified format. Supported\n\ 903178172Simp values are `berkeley' and `sysv'.\n\ 904178172Simp --help Display this help message and exit.\n\ 905178172Simp --radix=radix Display numeric values in the specified radix.\n\ 906178172Simp Supported values are: 8, 10 and 16.\n\ 907178172Simp --totals Show cumulative totals of section sizes.\n\ 908178172Simp --version Display a version identifier and exit.\n\ 909178172Simp -A Equivalent to `--format=sysv'.\n\ 910178172Simp -B Equivalent to `--format=berkeley'.\n\ 911178172Simp -V Equivalent to `--version'.\n\ 912178172Simp -d Equivalent to `--radix=10'.\n\ 913178172Simp -h Same as option --help.\n\ 914240177Sjhb -o Equivalent to `--radix=8'.\n\ 915178172Simp -t Equivalent to option --totals.\n\ 916240177Sjhb -x Equivalent to `--radix=16'.\n" 917178172Simp 918178172Simpstatic void 919178172Simpusage(void) 920178172Simp{ 921178172Simp (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); 922178172Simp exit(EXIT_FAILURE); 923178172Simp} 924178172Simp 925178172Simpstatic void 926178172Simpshow_version(void) 927178172Simp{ 928178172Simp (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version()); 929178172Simp exit(EXIT_SUCCESS); 930178172Simp} 931178172Simp