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