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