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