1 /*- 2 * Copyright (c) 2007 S.Sam Arun Raj 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 #include <assert.h> 28 #include <capsicum_helpers.h> 29 #include <err.h> 30 #include <fcntl.h> 31 #include <gelf.h> 32 #include <getopt.h> 33 #include <libelftc.h> 34 #include <stdint.h> 35 #include <stdio.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <unistd.h> 39 40 #include <libcasper.h> 41 #include <casper/cap_fileargs.h> 42 43 #include "_elftc.h" 44 45 ELFTC_VCSID("$Id: size.c 3458 2016-05-09 15:01:25Z emaste $"); 46 47 #define BUF_SIZE 1024 48 #define ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1)) 49 #define SIZE_VERSION_STRING "size 1.0" 50 51 enum return_code { 52 RETURN_OK, 53 RETURN_DATAERR, 54 RETURN_USAGE 55 }; 56 57 enum output_style { 58 STYLE_BERKELEY, 59 STYLE_SYSV 60 }; 61 62 enum radix_style { 63 RADIX_OCTAL, 64 RADIX_DECIMAL, 65 RADIX_HEX 66 }; 67 68 static uint64_t bss_size, data_size, text_size, total_size; 69 static uint64_t bss_size_total, data_size_total, text_size_total; 70 static int show_totals; 71 static int size_option; 72 static enum radix_style radix = RADIX_DECIMAL; 73 static enum output_style style = STYLE_BERKELEY; 74 75 static struct { 76 int row; 77 int col; 78 int *width; 79 char ***tbl; 80 } *tb; 81 82 enum { 83 OPT_FORMAT, 84 OPT_RADIX 85 }; 86 87 static struct option size_longopts[] = { 88 { "format", required_argument, &size_option, OPT_FORMAT }, 89 { "help", no_argument, NULL, 'h' }, 90 { "radix", required_argument, &size_option, OPT_RADIX }, 91 { "totals", no_argument, NULL, 't' }, 92 { "version", no_argument, NULL, 'V' }, 93 { NULL, 0, NULL, 0 } 94 }; 95 96 static void berkeley_calc(GElf_Shdr *); 97 static void berkeley_footer(const char *, const char *, const char *); 98 static void berkeley_header(void); 99 static void berkeley_totals(void); 100 static int handle_core(char const *, Elf *elf, GElf_Ehdr *); 101 static void handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **); 102 static int handle_elf(int, char const *); 103 static void handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t, 104 const char *); 105 static void show_version(void); 106 static void sysv_header(const char *, Elf_Arhdr *); 107 static void sysv_footer(void); 108 static void sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *); 109 static void usage(void); 110 static void tbl_new(int); 111 static void tbl_print(const char *, int); 112 static void tbl_print_num(uint64_t, enum radix_style, int); 113 static void tbl_append(void); 114 static void tbl_flush(void); 115 116 /* 117 * size utility using elf(3) and gelf(3) API to list section sizes and 118 * total in elf files. Supports only elf files (core dumps in elf 119 * included) that can be opened by libelf, other formats are not supported. 120 */ 121 int 122 main(int argc, char **argv) 123 { 124 cap_rights_t rights; 125 fileargs_t *fa; 126 int ch, fd, r, rc; 127 const char *fn; 128 char *defaultfn; 129 130 rc = RETURN_OK; 131 132 if (elf_version(EV_CURRENT) == EV_NONE) 133 errx(EXIT_FAILURE, "ELF library initialization failed: %s", 134 elf_errmsg(-1)); 135 136 while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts, 137 NULL)) != -1) 138 switch((char)ch) { 139 case 'A': 140 style = STYLE_SYSV; 141 break; 142 case 'B': 143 style = STYLE_BERKELEY; 144 break; 145 case 'V': 146 show_version(); 147 break; 148 case 'd': 149 radix = RADIX_DECIMAL; 150 break; 151 case 'o': 152 radix = RADIX_OCTAL; 153 break; 154 case 't': 155 show_totals = 1; 156 break; 157 case 'x': 158 radix = RADIX_HEX; 159 break; 160 case 0: 161 switch (size_option) { 162 case OPT_FORMAT: 163 if (*optarg == 's' || *optarg == 'S') 164 style = STYLE_SYSV; 165 else if (*optarg == 'b' || *optarg == 'B') 166 style = STYLE_BERKELEY; 167 else { 168 warnx("unrecognized format \"%s\".", 169 optarg); 170 usage(); 171 } 172 break; 173 case OPT_RADIX: 174 r = strtol(optarg, NULL, 10); 175 if (r == 8) 176 radix = RADIX_OCTAL; 177 else if (r == 10) 178 radix = RADIX_DECIMAL; 179 else if (r == 16) 180 radix = RADIX_HEX; 181 else { 182 warnx("unsupported radix \"%s\".", 183 optarg); 184 usage(); 185 } 186 break; 187 default: 188 err(EXIT_FAILURE, "Error in option handling."); 189 /*NOTREACHED*/ 190 } 191 break; 192 case 'h': 193 case '?': 194 default: 195 usage(); 196 /* NOTREACHED */ 197 } 198 argc -= optind; 199 argv += optind; 200 201 if (argc == 0) { 202 defaultfn = strdup("a.out"); 203 if (defaultfn == NULL) 204 err(EXIT_FAILURE, "strdup"); 205 argc = 1; 206 argv = &defaultfn; 207 } else { 208 defaultfn = NULL; 209 } 210 211 cap_rights_init(&rights, CAP_FSTAT, CAP_MMAP_R); 212 fa = fileargs_init(argc, argv, O_RDONLY, 0, &rights, FA_OPEN); 213 if (fa == NULL) 214 err(EXIT_FAILURE, "failed to initialize fileargs"); 215 216 caph_cache_catpages(); 217 if (caph_limit_stdio() < 0) 218 err(EXIT_FAILURE, "failed to limit stdio rights"); 219 if (caph_enter_casper() < 0) 220 err(EXIT_FAILURE, "failed to enter capability mode"); 221 222 for (; argc > 0; argc--, argv++) { 223 fn = argv[0]; 224 fd = fileargs_open(fa, fn); 225 if (fd < 0) { 226 warn("%s: Failed to open", fn); 227 continue; 228 } 229 rc = handle_elf(fd, fn); 230 if (rc != RETURN_OK) 231 warnx("%s: File format not recognized", fn); 232 } 233 if (style == STYLE_BERKELEY) { 234 if (show_totals) 235 berkeley_totals(); 236 tbl_flush(); 237 } 238 fileargs_free(fa); 239 free(defaultfn); 240 return (rc); 241 } 242 243 static int 244 xlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst, 245 Elf_Type type, size_t size) 246 { 247 Elf_Data src, dst; 248 249 src.d_buf = _src; 250 src.d_type = type; 251 src.d_version = elfhdr->e_version; 252 src.d_size = size; 253 dst.d_buf = _dst; 254 dst.d_version = elfhdr->e_version; 255 dst.d_size = size; 256 return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA]) != 257 NULL ? 0 : 1); 258 } 259 260 #define NOTE_OFFSET_32(nhdr, namesz, offset) \ 261 ((char *)nhdr + sizeof(Elf32_Nhdr) + \ 262 ELF_ALIGN((int32_t)namesz, 4) + offset) 263 264 #define NOTE_OFFSET_64(nhdr, namesz, offset) \ 265 ((char *)nhdr + sizeof(Elf32_Nhdr) + \ 266 ELF_ALIGN((int32_t)namesz, 8) + offset) 267 268 #define PID32(nhdr, namesz, offset) \ 269 (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr, \ 270 namesz, offset))); 271 272 #define PID64(nhdr, namesz, offset) \ 273 (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr, \ 274 namesz, offset))); 275 276 #define NEXT_NOTE(elfhdr, descsz, namesz, offset) do { \ 277 if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { \ 278 offset += ELF_ALIGN((int32_t)descsz, 4) + \ 279 sizeof(Elf32_Nhdr) + \ 280 ELF_ALIGN((int32_t)namesz, 4); \ 281 } else { \ 282 offset += ELF_ALIGN((int32_t)descsz, 8) + \ 283 sizeof(Elf32_Nhdr) + \ 284 ELF_ALIGN((int32_t)namesz, 8); \ 285 } \ 286 } while (0) 287 288 /* 289 * Parse individual note entries inside a PT_NOTE segment. 290 */ 291 static void 292 handle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, 293 char **cmd_line) 294 { 295 size_t max_size, segment_end; 296 uint64_t raw_size; 297 GElf_Off offset; 298 static pid_t pid; 299 uintptr_t ver; 300 Elf32_Nhdr *nhdr, nhdr_l; 301 static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/; 302 char buf[BUF_SIZE], *data, *name; 303 304 if (elf == NULL || elfhdr == NULL || phdr == NULL) 305 return; 306 307 data = elf_rawfile(elf, &max_size); 308 offset = phdr->p_offset; 309 if (offset >= max_size || phdr->p_filesz > max_size - offset) { 310 warnx("invalid PHDR offset"); 311 return; 312 } 313 segment_end = phdr->p_offset + phdr->p_filesz; 314 315 while (data != NULL && offset + sizeof(Elf32_Nhdr) < segment_end) { 316 nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset); 317 memset(&nhdr_l, 0, sizeof(Elf32_Nhdr)); 318 if (xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type, 319 ELF_T_WORD, sizeof(Elf32_Word)) != 0 || 320 xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz, 321 ELF_T_WORD, sizeof(Elf32_Word)) != 0 || 322 xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz, 323 ELF_T_WORD, sizeof(Elf32_Word)) != 0) 324 break; 325 326 if (offset + sizeof(Elf32_Nhdr) + 327 ELF_ALIGN(nhdr_l.n_namesz, 4) + 328 ELF_ALIGN(nhdr_l.n_descsz, 4) >= segment_end) { 329 warnx("invalid note header"); 330 return; 331 } 332 333 name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr)); 334 switch (nhdr_l.n_type) { 335 case NT_PRSTATUS: { 336 raw_size = 0; 337 if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD && 338 nhdr_l.n_namesz == 0x8 && 339 !strcmp(name,"FreeBSD")) { 340 if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { 341 raw_size = (uint64_t)*((uint32_t *) 342 (uintptr_t)(name + 343 ELF_ALIGN((int32_t) 344 nhdr_l.n_namesz, 4) + 8)); 345 ver = (uintptr_t)NOTE_OFFSET_32(nhdr, 346 nhdr_l.n_namesz,0); 347 if (*((int *)ver) == 1) 348 pid = PID32(nhdr, 349 nhdr_l.n_namesz, 24); 350 } else { 351 raw_size = *((uint64_t *)(uintptr_t) 352 (name + ELF_ALIGN((int32_t) 353 nhdr_l.n_namesz, 8) + 16)); 354 ver = (uintptr_t)NOTE_OFFSET_64(nhdr, 355 nhdr_l.n_namesz,0); 356 if (*((int *)ver) == 1) 357 pid = PID64(nhdr, 358 nhdr_l.n_namesz, 40); 359 } 360 (void)xlatetom(elf, elfhdr, &raw_size, 361 &raw_size, ELF_T_WORD, sizeof(uint64_t)); 362 (void)xlatetom(elf, elfhdr, &pid, &pid, 363 ELF_T_WORD, sizeof(pid_t)); 364 } 365 366 if (raw_size != 0 && style == STYLE_SYSV) { 367 (void) snprintf(buf, BUF_SIZE, "%s/%d", 368 ".reg", pid); 369 tbl_append(); 370 tbl_print(buf, 0); 371 tbl_print_num(raw_size, radix, 1); 372 tbl_print_num(0, radix, 2); 373 if (!reg_pseudo) { 374 tbl_append(); 375 tbl_print(".reg", 0); 376 tbl_print_num(raw_size, radix, 1); 377 tbl_print_num(0, radix, 2); 378 reg_pseudo = 1; 379 text_size_total += raw_size; 380 } 381 text_size_total += raw_size; 382 } 383 } 384 break; 385 case NT_FPREGSET: /* same as NT_PRFPREG */ 386 if (style == STYLE_SYSV) { 387 (void) snprintf(buf, BUF_SIZE, 388 "%s/%d", ".reg2", pid); 389 tbl_append(); 390 tbl_print(buf, 0); 391 tbl_print_num(nhdr_l.n_descsz, radix, 1); 392 tbl_print_num(0, radix, 2); 393 if (!reg2_pseudo) { 394 tbl_append(); 395 tbl_print(".reg2", 0); 396 tbl_print_num(nhdr_l.n_descsz, radix, 397 1); 398 tbl_print_num(0, radix, 2); 399 reg2_pseudo = 1; 400 text_size_total += nhdr_l.n_descsz; 401 } 402 text_size_total += nhdr_l.n_descsz; 403 } 404 break; 405 #if 0 406 case NT_AUXV: 407 if (style == STYLE_SYSV) { 408 tbl_append(); 409 tbl_print(".auxv", 0); 410 tbl_print_num(nhdr_l.n_descsz, radix, 1); 411 tbl_print_num(0, radix, 2); 412 text_size_total += nhdr_l.n_descsz; 413 } 414 break; 415 case NT_PRXFPREG: 416 if (style == STYLE_SYSV) { 417 (void) snprintf(buf, BUF_SIZE, "%s/%d", 418 ".reg-xfp", pid); 419 tbl_append(); 420 tbl_print(buf, 0); 421 tbl_print_num(nhdr_l.n_descsz, radix, 1); 422 tbl_print_num(0, radix, 2); 423 if (!regxfp_pseudo) { 424 tbl_append(); 425 tbl_print(".reg-xfp", 0); 426 tbl_print_num(nhdr_l.n_descsz, radix, 427 1); 428 tbl_print_num(0, radix, 2); 429 regxfp_pseudo = 1; 430 text_size_total += nhdr_l.n_descsz; 431 } 432 text_size_total += nhdr_l.n_descsz; 433 } 434 break; 435 case NT_PSINFO: 436 #endif 437 case NT_PRPSINFO: { 438 /* FreeBSD 64-bit */ 439 if (nhdr_l.n_descsz == 0x78 && 440 !strcmp(name,"FreeBSD")) { 441 *cmd_line = strdup(NOTE_OFFSET_64(nhdr, 442 nhdr_l.n_namesz, 33)); 443 /* FreeBSD 32-bit */ 444 } else if (nhdr_l.n_descsz == 0x6c && 445 !strcmp(name,"FreeBSD")) { 446 *cmd_line = strdup(NOTE_OFFSET_32(nhdr, 447 nhdr_l.n_namesz, 25)); 448 } 449 /* Strip any trailing spaces */ 450 if (*cmd_line != NULL) { 451 char *s; 452 453 s = *cmd_line + strlen(*cmd_line); 454 while (s > *cmd_line) { 455 if (*(s-1) != 0x20) break; 456 s--; 457 } 458 *s = 0; 459 } 460 break; 461 } 462 #if 0 463 case NT_PSTATUS: 464 case NT_LWPSTATUS: 465 #endif 466 default: 467 break; 468 } 469 NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset); 470 } 471 } 472 473 /* 474 * Handles program headers except for PT_NOTE, when sysv output style is 475 * chosen, prints out the segment name and length. For berkely output 476 * style only PT_LOAD segments are handled, and text, 477 * data, bss size is calculated for them. 478 */ 479 static void 480 handle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, 481 uint32_t idx, const char *name) 482 { 483 uint64_t addr, size; 484 int split; 485 char buf[BUF_SIZE]; 486 487 if (elf == NULL || elfhdr == NULL || phdr == NULL) 488 return; 489 490 split = (phdr->p_memsz > 0) && (phdr->p_filesz > 0) && 491 (phdr->p_memsz > phdr->p_filesz); 492 493 if (style == STYLE_SYSV) { 494 (void) snprintf(buf, BUF_SIZE, 495 "%s%d%s", name, idx, (split ? "a" : "")); 496 tbl_append(); 497 tbl_print(buf, 0); 498 tbl_print_num(phdr->p_filesz, radix, 1); 499 tbl_print_num(phdr->p_vaddr, radix, 2); 500 text_size_total += phdr->p_filesz; 501 if (split) { 502 size = phdr->p_memsz - phdr->p_filesz; 503 addr = phdr->p_vaddr + phdr->p_filesz; 504 (void) snprintf(buf, BUF_SIZE, "%s%d%s", name, 505 idx, "b"); 506 text_size_total += phdr->p_memsz - phdr->p_filesz; 507 tbl_append(); 508 tbl_print(buf, 0); 509 tbl_print_num(size, radix, 1); 510 tbl_print_num(addr, radix, 2); 511 } 512 } else { 513 if (phdr->p_type != PT_LOAD) 514 return; 515 if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) { 516 data_size += phdr->p_filesz; 517 if (split) 518 data_size += phdr->p_memsz - phdr->p_filesz; 519 } else { 520 text_size += phdr->p_filesz; 521 if (split) 522 text_size += phdr->p_memsz - phdr->p_filesz; 523 } 524 } 525 } 526 527 /* 528 * Given a core dump file, this function maps program headers to segments. 529 */ 530 static int 531 handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr) 532 { 533 GElf_Phdr phdr; 534 uint32_t i; 535 char *core_cmdline; 536 const char *seg_name; 537 538 if (name == NULL || elf == NULL || elfhdr == NULL) 539 return (RETURN_DATAERR); 540 if (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE) 541 return (RETURN_DATAERR); 542 543 seg_name = core_cmdline = NULL; 544 if (style == STYLE_SYSV) 545 sysv_header(name, NULL); 546 else 547 berkeley_header(); 548 549 for (i = 0; i < elfhdr->e_phnum; i++) { 550 if (gelf_getphdr(elf, i, &phdr) != NULL) { 551 if (phdr.p_type == PT_NOTE) { 552 handle_phdr(elf, elfhdr, &phdr, i, "note"); 553 handle_core_note(elf, elfhdr, &phdr, 554 &core_cmdline); 555 } else { 556 switch(phdr.p_type) { 557 case PT_NULL: 558 seg_name = "null"; 559 break; 560 case PT_LOAD: 561 seg_name = "load"; 562 break; 563 case PT_DYNAMIC: 564 seg_name = "dynamic"; 565 break; 566 case PT_INTERP: 567 seg_name = "interp"; 568 break; 569 case PT_SHLIB: 570 seg_name = "shlib"; 571 break; 572 case PT_PHDR: 573 seg_name = "phdr"; 574 break; 575 case PT_GNU_EH_FRAME: 576 seg_name = "eh_frame_hdr"; 577 break; 578 case PT_GNU_STACK: 579 seg_name = "stack"; 580 break; 581 default: 582 seg_name = "segment"; 583 } 584 handle_phdr(elf, elfhdr, &phdr, i, seg_name); 585 } 586 } 587 } 588 589 if (style == STYLE_BERKELEY) { 590 if (core_cmdline != NULL) { 591 berkeley_footer(core_cmdline, name, 592 "core file invoked as"); 593 } else { 594 berkeley_footer(core_cmdline, name, "core file"); 595 } 596 } else { 597 sysv_footer(); 598 if (core_cmdline != NULL) { 599 (void) printf(" (core file invoked as %s)\n\n", 600 core_cmdline); 601 } else { 602 (void) printf(" (core file)\n\n"); 603 } 604 } 605 free(core_cmdline); 606 return (RETURN_OK); 607 } 608 609 /* 610 * Given an elf object,ar(1) filename, and based on the output style 611 * and radix format the various sections and their length will be printed 612 * or the size of the text, data, bss sections will be printed out. 613 */ 614 static int 615 handle_elf(int fd, const char *name) 616 { 617 GElf_Ehdr elfhdr; 618 GElf_Shdr shdr; 619 Elf *elf, *elf1; 620 Elf_Arhdr *arhdr; 621 Elf_Scn *scn; 622 Elf_Cmd elf_cmd; 623 int exit_code; 624 625 elf_cmd = ELF_C_READ; 626 elf1 = elf_begin(fd, elf_cmd, NULL); 627 while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) { 628 arhdr = elf_getarhdr(elf); 629 if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) { 630 (void) elf_end(elf); 631 (void) elf_end(elf1); 632 (void) close(fd); 633 return (RETURN_DATAERR); 634 } 635 if (elf_kind(elf) != ELF_K_ELF || 636 (gelf_getehdr(elf, &elfhdr) == NULL)) { 637 elf_cmd = elf_next(elf); 638 (void) elf_end(elf); 639 warnx("%s: File format not recognized", 640 arhdr != NULL ? arhdr->ar_name : name); 641 continue; 642 } 643 /* Core dumps are handled separately */ 644 if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) { 645 exit_code = handle_core(name, elf, &elfhdr); 646 (void) elf_end(elf); 647 (void) elf_end(elf1); 648 (void) close(fd); 649 return (exit_code); 650 } else { 651 scn = NULL; 652 if (style == STYLE_BERKELEY) { 653 berkeley_header(); 654 while ((scn = elf_nextscn(elf, scn)) != NULL) { 655 if (gelf_getshdr(scn, &shdr) != NULL) 656 berkeley_calc(&shdr); 657 } 658 } else { 659 sysv_header(name, arhdr); 660 scn = NULL; 661 while ((scn = elf_nextscn(elf, scn)) != NULL) { 662 if (gelf_getshdr(scn, &shdr) != NULL) 663 sysv_calc(elf, &elfhdr, &shdr); 664 } 665 } 666 if (style == STYLE_BERKELEY) { 667 if (arhdr != NULL) { 668 berkeley_footer(name, arhdr->ar_name, 669 "ex"); 670 } else { 671 berkeley_footer(name, NULL, "ex"); 672 } 673 } else { 674 sysv_footer(); 675 } 676 } 677 elf_cmd = elf_next(elf); 678 (void) elf_end(elf); 679 } 680 (void) elf_end(elf1); 681 (void) close(fd); 682 return (RETURN_OK); 683 } 684 685 /* 686 * Sysv formatting helper functions. 687 */ 688 static void 689 sysv_header(const char *name, Elf_Arhdr *arhdr) 690 { 691 692 text_size_total = 0; 693 if (arhdr != NULL) 694 (void) printf("%s (ex %s):\n", arhdr->ar_name, name); 695 else 696 (void) printf("%s :\n", name); 697 tbl_new(3); 698 tbl_append(); 699 tbl_print("section", 0); 700 tbl_print("size", 1); 701 tbl_print("addr", 2); 702 } 703 704 static void 705 sysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr) 706 { 707 char *section_name; 708 709 section_name = elf_strptr(elf, elfhdr->e_shstrndx, 710 (size_t) shdr->sh_name); 711 if ((shdr->sh_type == SHT_SYMTAB || 712 shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA || 713 shdr->sh_type == SHT_REL) && shdr->sh_addr == 0) 714 return; 715 tbl_append(); 716 tbl_print(section_name, 0); 717 tbl_print_num(shdr->sh_size, radix, 1); 718 tbl_print_num(shdr->sh_addr, radix, 2); 719 text_size_total += shdr->sh_size; 720 } 721 722 static void 723 sysv_footer(void) 724 { 725 tbl_append(); 726 tbl_print("Total", 0); 727 tbl_print_num(text_size_total, radix, 1); 728 tbl_flush(); 729 putchar('\n'); 730 } 731 732 /* 733 * berkeley style output formatting helper functions. 734 */ 735 static void 736 berkeley_header(void) 737 { 738 static int printed; 739 740 text_size = data_size = bss_size = 0; 741 if (!printed) { 742 tbl_new(6); 743 tbl_append(); 744 tbl_print("text", 0); 745 tbl_print("data", 1); 746 tbl_print("bss", 2); 747 if (radix == RADIX_OCTAL) 748 tbl_print("oct", 3); 749 else 750 tbl_print("dec", 3); 751 tbl_print("hex", 4); 752 tbl_print("filename", 5); 753 printed = 1; 754 } 755 } 756 757 static void 758 berkeley_calc(GElf_Shdr *shdr) 759 { 760 if (shdr != NULL) { 761 if (!(shdr->sh_flags & SHF_ALLOC)) 762 return; 763 if ((shdr->sh_flags & SHF_ALLOC) && 764 ((shdr->sh_flags & SHF_EXECINSTR) || 765 !(shdr->sh_flags & SHF_WRITE))) 766 text_size += shdr->sh_size; 767 else if ((shdr->sh_flags & SHF_ALLOC) && 768 (shdr->sh_flags & SHF_WRITE) && 769 (shdr->sh_type != SHT_NOBITS)) 770 data_size += shdr->sh_size; 771 else 772 bss_size += shdr->sh_size; 773 } 774 } 775 776 static void 777 berkeley_totals(void) 778 { 779 uint64_t grand_total; 780 781 grand_total = text_size_total + data_size_total + bss_size_total; 782 tbl_append(); 783 tbl_print_num(text_size_total, radix, 0); 784 tbl_print_num(data_size_total, radix, 1); 785 tbl_print_num(bss_size_total, radix, 2); 786 if (radix == RADIX_OCTAL) 787 tbl_print_num(grand_total, RADIX_OCTAL, 3); 788 else 789 tbl_print_num(grand_total, RADIX_DECIMAL, 3); 790 tbl_print_num(grand_total, RADIX_HEX, 4); 791 } 792 793 static void 794 berkeley_footer(const char *name, const char *ar_name, const char *msg) 795 { 796 char buf[BUF_SIZE]; 797 798 total_size = text_size + data_size + bss_size; 799 if (show_totals) { 800 text_size_total += text_size; 801 bss_size_total += bss_size; 802 data_size_total += data_size; 803 } 804 805 tbl_append(); 806 tbl_print_num(text_size, radix, 0); 807 tbl_print_num(data_size, radix, 1); 808 tbl_print_num(bss_size, radix, 2); 809 if (radix == RADIX_OCTAL) 810 tbl_print_num(total_size, RADIX_OCTAL, 3); 811 else 812 tbl_print_num(total_size, RADIX_DECIMAL, 3); 813 tbl_print_num(total_size, RADIX_HEX, 4); 814 if (ar_name != NULL && name != NULL) 815 (void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg, 816 name); 817 else if (ar_name != NULL && name == NULL) 818 (void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg); 819 else 820 (void) snprintf(buf, BUF_SIZE, "%s", name); 821 tbl_print(buf, 5); 822 } 823 824 825 static void 826 tbl_new(int col) 827 { 828 829 assert(tb == NULL); 830 assert(col > 0); 831 if ((tb = calloc(1, sizeof(*tb))) == NULL) 832 err(EXIT_FAILURE, "calloc"); 833 if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL) 834 err(EXIT_FAILURE, "calloc"); 835 if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL) 836 err(EXIT_FAILURE, "calloc"); 837 tb->col = col; 838 tb->row = 0; 839 } 840 841 static void 842 tbl_print(const char *s, int col) 843 { 844 int len; 845 846 assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col); 847 assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL); 848 if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL) 849 err(EXIT_FAILURE, "strdup"); 850 len = strlen(s); 851 if (len > tb->width[col]) 852 tb->width[col] = len; 853 } 854 855 static void 856 tbl_print_num(uint64_t num, enum radix_style rad, int col) 857 { 858 char buf[BUF_SIZE]; 859 860 (void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" : 861 ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num); 862 tbl_print(buf, col); 863 } 864 865 static void 866 tbl_append(void) 867 { 868 int i; 869 870 assert(tb != NULL && tb->col > 0); 871 tb->row++; 872 for (i = 0; i < tb->col; i++) { 873 tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row); 874 if (tb->tbl[i] == NULL) 875 err(EXIT_FAILURE, "realloc"); 876 tb->tbl[i][tb->row - 1] = NULL; 877 } 878 } 879 880 static void 881 tbl_flush(void) 882 { 883 const char *str; 884 int i, j; 885 886 if (tb == NULL) 887 return; 888 889 assert(tb->col > 0); 890 for (i = 0; i < tb->row; i++) { 891 if (style == STYLE_BERKELEY) 892 printf(" "); 893 for (j = 0; j < tb->col; j++) { 894 str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : ""); 895 if (style == STYLE_SYSV && j == 0) 896 printf("%-*s", tb->width[j], str); 897 else if (style == STYLE_BERKELEY && j == tb->col - 1) 898 printf("%s", str); 899 else 900 printf("%*s", tb->width[j], str); 901 if (j == tb->col -1) 902 putchar('\n'); 903 else 904 printf(" "); 905 } 906 } 907 908 for (i = 0; i < tb->col; i++) { 909 for (j = 0; j < tb->row; j++) { 910 if (tb->tbl[i][j]) 911 free(tb->tbl[i][j]); 912 } 913 free(tb->tbl[i]); 914 } 915 free(tb->tbl); 916 free(tb->width); 917 free(tb); 918 tb = NULL; 919 } 920 921 #define USAGE_MESSAGE "\ 922 Usage: %s [options] file ...\n\ 923 Display sizes of ELF sections.\n\n\ 924 Options:\n\ 925 --format=format Display output in specified format. Supported\n\ 926 values are `berkeley' and `sysv'.\n\ 927 --help Display this help message and exit.\n\ 928 --radix=radix Display numeric values in the specified radix.\n\ 929 Supported values are: 8, 10 and 16.\n\ 930 --totals Show cumulative totals of section sizes.\n\ 931 --version Display a version identifier and exit.\n\ 932 -A Equivalent to `--format=sysv'.\n\ 933 -B Equivalent to `--format=berkeley'.\n\ 934 -V Equivalent to `--version'.\n\ 935 -d Equivalent to `--radix=10'.\n\ 936 -h Same as option --help.\n\ 937 -o Equivalent to `--radix=8'.\n\ 938 -t Equivalent to option --totals.\n\ 939 -x Equivalent to `--radix=16'.\n" 940 941 static void 942 usage(void) 943 { 944 (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); 945 exit(EXIT_FAILURE); 946 } 947 948 static void 949 show_version(void) 950 { 951 (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version()); 952 exit(EXIT_SUCCESS); 953 } 954