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 Elf_Data * 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 } 258 259 #define NOTE_OFFSET_32(nhdr, namesz, offset) \ 260 ((char *)nhdr + sizeof(Elf32_Nhdr) + \ 261 ELF_ALIGN((int32_t)namesz, 4) + offset) 262 263 #define NOTE_OFFSET_64(nhdr, namesz, offset) \ 264 ((char *)nhdr + sizeof(Elf32_Nhdr) + \ 265 ELF_ALIGN((int32_t)namesz, 8) + offset) 266 267 #define PID32(nhdr, namesz, offset) \ 268 (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr, \ 269 namesz, offset))); 270 271 #define PID64(nhdr, namesz, offset) \ 272 (pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr, \ 273 namesz, offset))); 274 275 #define NEXT_NOTE(elfhdr, descsz, namesz, offset) do { \ 276 if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { \ 277 offset += ELF_ALIGN((int32_t)descsz, 4) + \ 278 sizeof(Elf32_Nhdr) + \ 279 ELF_ALIGN((int32_t)namesz, 4); \ 280 } else { \ 281 offset += ELF_ALIGN((int32_t)descsz, 8) + \ 282 sizeof(Elf32_Nhdr) + \ 283 ELF_ALIGN((int32_t)namesz, 8); \ 284 } \ 285 } while (0) 286 287 /* 288 * Parse individual note entries inside a PT_NOTE segment. 289 */ 290 static void 291 handle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, 292 char **cmd_line) 293 { 294 size_t max_size, segment_end; 295 uint64_t raw_size; 296 GElf_Off offset; 297 static pid_t pid; 298 uintptr_t ver; 299 Elf32_Nhdr *nhdr, nhdr_l; 300 static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/; 301 char buf[BUF_SIZE], *data, *name; 302 303 if (elf == NULL || elfhdr == NULL || phdr == NULL) 304 return; 305 306 data = elf_rawfile(elf, &max_size); 307 offset = phdr->p_offset; 308 if (offset >= max_size || phdr->p_filesz > max_size - offset) { 309 warnx("invalid PHDR offset"); 310 return; 311 } 312 segment_end = phdr->p_offset + phdr->p_filesz; 313 314 while (data != NULL && offset + sizeof(Elf32_Nhdr) < segment_end) { 315 nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset); 316 memset(&nhdr_l, 0, sizeof(Elf32_Nhdr)); 317 if (!xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type, 318 ELF_T_WORD, sizeof(Elf32_Word)) || 319 !xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz, 320 ELF_T_WORD, sizeof(Elf32_Word)) || 321 !xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz, 322 ELF_T_WORD, sizeof(Elf32_Word))) 323 break; 324 325 if (offset + sizeof(Elf32_Nhdr) + 326 ELF_ALIGN(nhdr_l.n_namesz, 4) + 327 ELF_ALIGN(nhdr_l.n_descsz, 4) >= segment_end) { 328 warnx("invalid note header"); 329 return; 330 } 331 332 name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr)); 333 switch (nhdr_l.n_type) { 334 case NT_PRSTATUS: { 335 raw_size = 0; 336 if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD && 337 nhdr_l.n_namesz == 0x8 && 338 !strcmp(name,"FreeBSD")) { 339 if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { 340 raw_size = (uint64_t)*((uint32_t *) 341 (uintptr_t)(name + 342 ELF_ALIGN((int32_t) 343 nhdr_l.n_namesz, 4) + 8)); 344 ver = (uintptr_t)NOTE_OFFSET_32(nhdr, 345 nhdr_l.n_namesz,0); 346 if (*((int *)ver) == 1) 347 pid = PID32(nhdr, 348 nhdr_l.n_namesz, 24); 349 } else { 350 raw_size = *((uint64_t *)(uintptr_t) 351 (name + ELF_ALIGN((int32_t) 352 nhdr_l.n_namesz, 8) + 16)); 353 ver = (uintptr_t)NOTE_OFFSET_64(nhdr, 354 nhdr_l.n_namesz,0); 355 if (*((int *)ver) == 1) 356 pid = PID64(nhdr, 357 nhdr_l.n_namesz, 40); 358 } 359 xlatetom(elf, elfhdr, &raw_size, &raw_size, 360 ELF_T_WORD, sizeof(uint64_t)); 361 xlatetom(elf, elfhdr, &pid, &pid, ELF_T_WORD, 362 sizeof(pid_t)); 363 } 364 365 if (raw_size != 0 && style == STYLE_SYSV) { 366 (void) snprintf(buf, BUF_SIZE, "%s/%d", 367 ".reg", pid); 368 tbl_append(); 369 tbl_print(buf, 0); 370 tbl_print_num(raw_size, radix, 1); 371 tbl_print_num(0, radix, 2); 372 if (!reg_pseudo) { 373 tbl_append(); 374 tbl_print(".reg", 0); 375 tbl_print_num(raw_size, radix, 1); 376 tbl_print_num(0, radix, 2); 377 reg_pseudo = 1; 378 text_size_total += raw_size; 379 } 380 text_size_total += raw_size; 381 } 382 } 383 break; 384 case NT_FPREGSET: /* same as NT_PRFPREG */ 385 if (style == STYLE_SYSV) { 386 (void) snprintf(buf, BUF_SIZE, 387 "%s/%d", ".reg2", pid); 388 tbl_append(); 389 tbl_print(buf, 0); 390 tbl_print_num(nhdr_l.n_descsz, radix, 1); 391 tbl_print_num(0, radix, 2); 392 if (!reg2_pseudo) { 393 tbl_append(); 394 tbl_print(".reg2", 0); 395 tbl_print_num(nhdr_l.n_descsz, radix, 396 1); 397 tbl_print_num(0, radix, 2); 398 reg2_pseudo = 1; 399 text_size_total += nhdr_l.n_descsz; 400 } 401 text_size_total += nhdr_l.n_descsz; 402 } 403 break; 404 #if 0 405 case NT_AUXV: 406 if (style == STYLE_SYSV) { 407 tbl_append(); 408 tbl_print(".auxv", 0); 409 tbl_print_num(nhdr_l.n_descsz, radix, 1); 410 tbl_print_num(0, radix, 2); 411 text_size_total += nhdr_l.n_descsz; 412 } 413 break; 414 case NT_PRXFPREG: 415 if (style == STYLE_SYSV) { 416 (void) snprintf(buf, BUF_SIZE, "%s/%d", 417 ".reg-xfp", pid); 418 tbl_append(); 419 tbl_print(buf, 0); 420 tbl_print_num(nhdr_l.n_descsz, radix, 1); 421 tbl_print_num(0, radix, 2); 422 if (!regxfp_pseudo) { 423 tbl_append(); 424 tbl_print(".reg-xfp", 0); 425 tbl_print_num(nhdr_l.n_descsz, radix, 426 1); 427 tbl_print_num(0, radix, 2); 428 regxfp_pseudo = 1; 429 text_size_total += nhdr_l.n_descsz; 430 } 431 text_size_total += nhdr_l.n_descsz; 432 } 433 break; 434 case NT_PSINFO: 435 #endif 436 case NT_PRPSINFO: { 437 /* FreeBSD 64-bit */ 438 if (nhdr_l.n_descsz == 0x78 && 439 !strcmp(name,"FreeBSD")) { 440 *cmd_line = strdup(NOTE_OFFSET_64(nhdr, 441 nhdr_l.n_namesz, 33)); 442 /* FreeBSD 32-bit */ 443 } else if (nhdr_l.n_descsz == 0x6c && 444 !strcmp(name,"FreeBSD")) { 445 *cmd_line = strdup(NOTE_OFFSET_32(nhdr, 446 nhdr_l.n_namesz, 25)); 447 } 448 /* Strip any trailing spaces */ 449 if (*cmd_line != NULL) { 450 char *s; 451 452 s = *cmd_line + strlen(*cmd_line); 453 while (s > *cmd_line) { 454 if (*(s-1) != 0x20) break; 455 s--; 456 } 457 *s = 0; 458 } 459 break; 460 } 461 #if 0 462 case NT_PSTATUS: 463 case NT_LWPSTATUS: 464 #endif 465 default: 466 break; 467 } 468 NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset); 469 } 470 } 471 472 /* 473 * Handles program headers except for PT_NOTE, when sysv output style is 474 * chosen, prints out the segment name and length. For berkely output 475 * style only PT_LOAD segments are handled, and text, 476 * data, bss size is calculated for them. 477 */ 478 static void 479 handle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr, 480 uint32_t idx, const char *name) 481 { 482 uint64_t addr, size; 483 int split; 484 char buf[BUF_SIZE]; 485 486 if (elf == NULL || elfhdr == NULL || phdr == NULL) 487 return; 488 489 split = (phdr->p_memsz > 0) && (phdr->p_filesz > 0) && 490 (phdr->p_memsz > phdr->p_filesz); 491 492 if (style == STYLE_SYSV) { 493 (void) snprintf(buf, BUF_SIZE, 494 "%s%d%s", name, idx, (split ? "a" : "")); 495 tbl_append(); 496 tbl_print(buf, 0); 497 tbl_print_num(phdr->p_filesz, radix, 1); 498 tbl_print_num(phdr->p_vaddr, radix, 2); 499 text_size_total += phdr->p_filesz; 500 if (split) { 501 size = phdr->p_memsz - phdr->p_filesz; 502 addr = phdr->p_vaddr + phdr->p_filesz; 503 (void) snprintf(buf, BUF_SIZE, "%s%d%s", name, 504 idx, "b"); 505 text_size_total += phdr->p_memsz - phdr->p_filesz; 506 tbl_append(); 507 tbl_print(buf, 0); 508 tbl_print_num(size, radix, 1); 509 tbl_print_num(addr, radix, 2); 510 } 511 } else { 512 if (phdr->p_type != PT_LOAD) 513 return; 514 if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) { 515 data_size += phdr->p_filesz; 516 if (split) 517 data_size += phdr->p_memsz - phdr->p_filesz; 518 } else { 519 text_size += phdr->p_filesz; 520 if (split) 521 text_size += phdr->p_memsz - phdr->p_filesz; 522 } 523 } 524 } 525 526 /* 527 * Given a core dump file, this function maps program headers to segments. 528 */ 529 static int 530 handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr) 531 { 532 GElf_Phdr phdr; 533 uint32_t i; 534 char *core_cmdline; 535 const char *seg_name; 536 537 if (name == NULL || elf == NULL || elfhdr == NULL) 538 return (RETURN_DATAERR); 539 if (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE) 540 return (RETURN_DATAERR); 541 542 seg_name = core_cmdline = NULL; 543 if (style == STYLE_SYSV) 544 sysv_header(name, NULL); 545 else 546 berkeley_header(); 547 548 for (i = 0; i < elfhdr->e_phnum; i++) { 549 if (gelf_getphdr(elf, i, &phdr) != NULL) { 550 if (phdr.p_type == PT_NOTE) { 551 handle_phdr(elf, elfhdr, &phdr, i, "note"); 552 handle_core_note(elf, elfhdr, &phdr, 553 &core_cmdline); 554 } else { 555 switch(phdr.p_type) { 556 case PT_NULL: 557 seg_name = "null"; 558 break; 559 case PT_LOAD: 560 seg_name = "load"; 561 break; 562 case PT_DYNAMIC: 563 seg_name = "dynamic"; 564 break; 565 case PT_INTERP: 566 seg_name = "interp"; 567 break; 568 case PT_SHLIB: 569 seg_name = "shlib"; 570 break; 571 case PT_PHDR: 572 seg_name = "phdr"; 573 break; 574 case PT_GNU_EH_FRAME: 575 seg_name = "eh_frame_hdr"; 576 break; 577 case PT_GNU_STACK: 578 seg_name = "stack"; 579 break; 580 default: 581 seg_name = "segment"; 582 } 583 handle_phdr(elf, elfhdr, &phdr, i, seg_name); 584 } 585 } 586 } 587 588 if (style == STYLE_BERKELEY) { 589 if (core_cmdline != NULL) { 590 berkeley_footer(core_cmdline, name, 591 "core file invoked as"); 592 } else { 593 berkeley_footer(core_cmdline, name, "core file"); 594 } 595 } else { 596 sysv_footer(); 597 if (core_cmdline != NULL) { 598 (void) printf(" (core file invoked as %s)\n\n", 599 core_cmdline); 600 } else { 601 (void) printf(" (core file)\n\n"); 602 } 603 } 604 free(core_cmdline); 605 return (RETURN_OK); 606 } 607 608 /* 609 * Given an elf object,ar(1) filename, and based on the output style 610 * and radix format the various sections and their length will be printed 611 * or the size of the text, data, bss sections will be printed out. 612 */ 613 static int 614 handle_elf(int fd, const char *name) 615 { 616 GElf_Ehdr elfhdr; 617 GElf_Shdr shdr; 618 Elf *elf, *elf1; 619 Elf_Arhdr *arhdr; 620 Elf_Scn *scn; 621 Elf_Cmd elf_cmd; 622 int exit_code; 623 624 elf_cmd = ELF_C_READ; 625 elf1 = elf_begin(fd, elf_cmd, NULL); 626 while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) { 627 arhdr = elf_getarhdr(elf); 628 if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) { 629 (void) elf_end(elf); 630 (void) elf_end(elf1); 631 (void) close(fd); 632 return (RETURN_DATAERR); 633 } 634 if (elf_kind(elf) != ELF_K_ELF || 635 (gelf_getehdr(elf, &elfhdr) == NULL)) { 636 elf_cmd = elf_next(elf); 637 (void) elf_end(elf); 638 warnx("%s: File format not recognized", 639 arhdr != NULL ? arhdr->ar_name : name); 640 continue; 641 } 642 /* Core dumps are handled separately */ 643 if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) { 644 exit_code = handle_core(name, elf, &elfhdr); 645 (void) elf_end(elf); 646 (void) elf_end(elf1); 647 (void) close(fd); 648 return (exit_code); 649 } else { 650 scn = NULL; 651 if (style == STYLE_BERKELEY) { 652 berkeley_header(); 653 while ((scn = elf_nextscn(elf, scn)) != NULL) { 654 if (gelf_getshdr(scn, &shdr) != NULL) 655 berkeley_calc(&shdr); 656 } 657 } else { 658 sysv_header(name, arhdr); 659 scn = NULL; 660 while ((scn = elf_nextscn(elf, scn)) != NULL) { 661 if (gelf_getshdr(scn, &shdr) != NULL) 662 sysv_calc(elf, &elfhdr, &shdr); 663 } 664 } 665 if (style == STYLE_BERKELEY) { 666 if (arhdr != NULL) { 667 berkeley_footer(name, arhdr->ar_name, 668 "ex"); 669 } else { 670 berkeley_footer(name, NULL, "ex"); 671 } 672 } else { 673 sysv_footer(); 674 } 675 } 676 elf_cmd = elf_next(elf); 677 (void) elf_end(elf); 678 } 679 (void) elf_end(elf1); 680 (void) close(fd); 681 return (RETURN_OK); 682 } 683 684 /* 685 * Sysv formatting helper functions. 686 */ 687 static void 688 sysv_header(const char *name, Elf_Arhdr *arhdr) 689 { 690 691 text_size_total = 0; 692 if (arhdr != NULL) 693 (void) printf("%s (ex %s):\n", arhdr->ar_name, name); 694 else 695 (void) printf("%s :\n", name); 696 tbl_new(3); 697 tbl_append(); 698 tbl_print("section", 0); 699 tbl_print("size", 1); 700 tbl_print("addr", 2); 701 } 702 703 static void 704 sysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr) 705 { 706 char *section_name; 707 708 section_name = elf_strptr(elf, elfhdr->e_shstrndx, 709 (size_t) shdr->sh_name); 710 if ((shdr->sh_type == SHT_SYMTAB || 711 shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA || 712 shdr->sh_type == SHT_REL) && shdr->sh_addr == 0) 713 return; 714 tbl_append(); 715 tbl_print(section_name, 0); 716 tbl_print_num(shdr->sh_size, radix, 1); 717 tbl_print_num(shdr->sh_addr, radix, 2); 718 text_size_total += shdr->sh_size; 719 } 720 721 static void 722 sysv_footer(void) 723 { 724 tbl_append(); 725 tbl_print("Total", 0); 726 tbl_print_num(text_size_total, radix, 1); 727 tbl_flush(); 728 putchar('\n'); 729 } 730 731 /* 732 * berkeley style output formatting helper functions. 733 */ 734 static void 735 berkeley_header(void) 736 { 737 static int printed; 738 739 text_size = data_size = bss_size = 0; 740 if (!printed) { 741 tbl_new(6); 742 tbl_append(); 743 tbl_print("text", 0); 744 tbl_print("data", 1); 745 tbl_print("bss", 2); 746 if (radix == RADIX_OCTAL) 747 tbl_print("oct", 3); 748 else 749 tbl_print("dec", 3); 750 tbl_print("hex", 4); 751 tbl_print("filename", 5); 752 printed = 1; 753 } 754 } 755 756 static void 757 berkeley_calc(GElf_Shdr *shdr) 758 { 759 if (shdr != NULL) { 760 if (!(shdr->sh_flags & SHF_ALLOC)) 761 return; 762 if ((shdr->sh_flags & SHF_ALLOC) && 763 ((shdr->sh_flags & SHF_EXECINSTR) || 764 !(shdr->sh_flags & SHF_WRITE))) 765 text_size += shdr->sh_size; 766 else if ((shdr->sh_flags & SHF_ALLOC) && 767 (shdr->sh_flags & SHF_WRITE) && 768 (shdr->sh_type != SHT_NOBITS)) 769 data_size += shdr->sh_size; 770 else 771 bss_size += shdr->sh_size; 772 } 773 } 774 775 static void 776 berkeley_totals(void) 777 { 778 uint64_t grand_total; 779 780 grand_total = text_size_total + data_size_total + bss_size_total; 781 tbl_append(); 782 tbl_print_num(text_size_total, radix, 0); 783 tbl_print_num(data_size_total, radix, 1); 784 tbl_print_num(bss_size_total, radix, 2); 785 if (radix == RADIX_OCTAL) 786 tbl_print_num(grand_total, RADIX_OCTAL, 3); 787 else 788 tbl_print_num(grand_total, RADIX_DECIMAL, 3); 789 tbl_print_num(grand_total, RADIX_HEX, 4); 790 } 791 792 static void 793 berkeley_footer(const char *name, const char *ar_name, const char *msg) 794 { 795 char buf[BUF_SIZE]; 796 797 total_size = text_size + data_size + bss_size; 798 if (show_totals) { 799 text_size_total += text_size; 800 bss_size_total += bss_size; 801 data_size_total += data_size; 802 } 803 804 tbl_append(); 805 tbl_print_num(text_size, radix, 0); 806 tbl_print_num(data_size, radix, 1); 807 tbl_print_num(bss_size, radix, 2); 808 if (radix == RADIX_OCTAL) 809 tbl_print_num(total_size, RADIX_OCTAL, 3); 810 else 811 tbl_print_num(total_size, RADIX_DECIMAL, 3); 812 tbl_print_num(total_size, RADIX_HEX, 4); 813 if (ar_name != NULL && name != NULL) 814 (void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg, 815 name); 816 else if (ar_name != NULL && name == NULL) 817 (void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg); 818 else 819 (void) snprintf(buf, BUF_SIZE, "%s", name); 820 tbl_print(buf, 5); 821 } 822 823 824 static void 825 tbl_new(int col) 826 { 827 828 assert(tb == NULL); 829 assert(col > 0); 830 if ((tb = calloc(1, sizeof(*tb))) == NULL) 831 err(EXIT_FAILURE, "calloc"); 832 if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL) 833 err(EXIT_FAILURE, "calloc"); 834 if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL) 835 err(EXIT_FAILURE, "calloc"); 836 tb->col = col; 837 tb->row = 0; 838 } 839 840 static void 841 tbl_print(const char *s, int col) 842 { 843 int len; 844 845 assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col); 846 assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL); 847 if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL) 848 err(EXIT_FAILURE, "strdup"); 849 len = strlen(s); 850 if (len > tb->width[col]) 851 tb->width[col] = len; 852 } 853 854 static void 855 tbl_print_num(uint64_t num, enum radix_style rad, int col) 856 { 857 char buf[BUF_SIZE]; 858 859 (void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" : 860 ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num); 861 tbl_print(buf, col); 862 } 863 864 static void 865 tbl_append(void) 866 { 867 int i; 868 869 assert(tb != NULL && tb->col > 0); 870 tb->row++; 871 for (i = 0; i < tb->col; i++) { 872 tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row); 873 if (tb->tbl[i] == NULL) 874 err(EXIT_FAILURE, "realloc"); 875 tb->tbl[i][tb->row - 1] = NULL; 876 } 877 } 878 879 static void 880 tbl_flush(void) 881 { 882 const char *str; 883 int i, j; 884 885 if (tb == NULL) 886 return; 887 888 assert(tb->col > 0); 889 for (i = 0; i < tb->row; i++) { 890 if (style == STYLE_BERKELEY) 891 printf(" "); 892 for (j = 0; j < tb->col; j++) { 893 str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : ""); 894 if (style == STYLE_SYSV && j == 0) 895 printf("%-*s", tb->width[j], str); 896 else if (style == STYLE_BERKELEY && j == tb->col - 1) 897 printf("%s", str); 898 else 899 printf("%*s", tb->width[j], str); 900 if (j == tb->col -1) 901 putchar('\n'); 902 else 903 printf(" "); 904 } 905 } 906 907 for (i = 0; i < tb->col; i++) { 908 for (j = 0; j < tb->row; j++) { 909 if (tb->tbl[i][j]) 910 free(tb->tbl[i][j]); 911 } 912 free(tb->tbl[i]); 913 } 914 free(tb->tbl); 915 free(tb->width); 916 free(tb); 917 tb = NULL; 918 } 919 920 #define USAGE_MESSAGE "\ 921 Usage: %s [options] file ...\n\ 922 Display sizes of ELF sections.\n\n\ 923 Options:\n\ 924 --format=format Display output in specified format. Supported\n\ 925 values are `berkeley' and `sysv'.\n\ 926 --help Display this help message and exit.\n\ 927 --radix=radix Display numeric values in the specified radix.\n\ 928 Supported values are: 8, 10 and 16.\n\ 929 --totals Show cumulative totals of section sizes.\n\ 930 --version Display a version identifier and exit.\n\ 931 -A Equivalent to `--format=sysv'.\n\ 932 -B Equivalent to `--format=berkeley'.\n\ 933 -V Equivalent to `--version'.\n\ 934 -d Equivalent to `--radix=10'.\n\ 935 -h Same as option --help.\n\ 936 -o Equivalent to `--radix=8'.\n\ 937 -t Equivalent to option --totals.\n\ 938 -x Equivalent to `--radix=16'.\n" 939 940 static void 941 usage(void) 942 { 943 (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME()); 944 exit(EXIT_FAILURE); 945 } 946 947 static void 948 show_version(void) 949 { 950 (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version()); 951 exit(EXIT_SUCCESS); 952 } 953