1/* Print size information from ELF file. 2 Copyright (C) 2000-2007,2009,2012,2014,2015 Red Hat, Inc. 3 This file is part of elfutils. 4 Written by Ulrich Drepper <drepper@redhat.com>, 2000. 5 6 This file is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 3 of the License, or 9 (at your option) any later version. 10 11 elfutils is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 18 19#ifdef HAVE_CONFIG_H 20# include <config.h> 21#endif 22 23#include <argp.h> 24#include <fcntl.h> 25#include <gelf.h> 26#include <inttypes.h> 27#include <libelf.h> 28#include <locale.h> 29#include <stdbool.h> 30#include <stdio.h> 31#include <stdio_ext.h> 32#include <stdlib.h> 33#include <string.h> 34#include <unistd.h> 35 36#include <system.h> 37#include <printversion.h> 38 39/* Name and version of program. */ 40ARGP_PROGRAM_VERSION_HOOK_DEF = print_version; 41 42/* Bug report address. */ 43ARGP_PROGRAM_BUG_ADDRESS_DEF = PACKAGE_BUGREPORT; 44 45 46/* Values for the parameters which have no short form. */ 47#define OPT_FORMAT 0x100 48#define OPT_RADIX 0x101 49 50/* Definitions of arguments for argp functions. */ 51static const struct argp_option options[] = 52{ 53 { NULL, 0, NULL, 0, N_("Output format:"), 0 }, 54 { "format", OPT_FORMAT, "FORMAT", 0, 55 N_("Use the output format FORMAT. FORMAT can be `bsd' or `sysv'. " 56 "The default is `bsd'"), 0 }, 57 { NULL, 'A', NULL, 0, N_("Same as `--format=sysv'"), 0 }, 58 { NULL, 'B', NULL, 0, N_("Same as `--format=bsd'"), 0 }, 59 { "radix", OPT_RADIX, "RADIX", 0, N_("Use RADIX for printing symbol values"), 60 0}, 61 { NULL, 'd', NULL, 0, N_("Same as `--radix=10'"), 0 }, 62 { NULL, 'o', NULL, 0, N_("Same as `--radix=8'"), 0 }, 63 { NULL, 'x', NULL, 0, N_("Same as `--radix=16'"), 0 }, 64 { NULL, 'f', NULL, 0, 65 N_("Similar to `--format=sysv' output but in one line"), 0 }, 66 67 { NULL, 0, NULL, 0, N_("Output options:"), 0 }, 68 { NULL, 'F', NULL, 0, 69 N_("Print size and permission flags for loadable segments"), 0 }, 70 { "totals", 't', NULL, 0, N_("Display the total sizes (bsd only)"), 0 }, 71 { NULL, 0, NULL, 0, NULL, 0 } 72}; 73 74/* Short description of program. */ 75static const char doc[] = N_("\ 76List section sizes of FILEs (a.out by default)."); 77 78/* Strings for arguments in help texts. */ 79static const char args_doc[] = N_("[FILE...]"); 80 81/* Prototype for option handler. */ 82static error_t parse_opt (int key, char *arg, struct argp_state *state); 83 84/* Data structure to communicate with argp functions. */ 85static struct argp argp = 86{ 87 options, parse_opt, args_doc, doc, NULL, NULL, NULL 88}; 89 90 91/* Print symbols in file named FNAME. */ 92static int process_file (const char *fname); 93 94/* Handle content of archive. */ 95static int handle_ar (int fd, Elf *elf, const char *prefix, const char *fname); 96 97/* Handle ELF file. */ 98static void handle_elf (Elf *elf, const char *fullname, const char *fname); 99 100/* Show total size. */ 101static void show_bsd_totals (void); 102 103#define INTERNAL_ERROR(fname) \ 104 error_exit (0, _("%s: INTERNAL ERROR %d (%s): %s"), \ 105 fname, __LINE__, PACKAGE_VERSION, elf_errmsg (-1)) 106 107 108/* User-selectable options. */ 109 110/* The selected output format. */ 111static enum 112{ 113 format_bsd = 0, 114 format_sysv, 115 format_sysv_one_line, 116 format_segments 117} format; 118 119/* Radix for printed numbers. */ 120static enum 121{ 122 radix_decimal = 0, 123 radix_hex, 124 radix_octal 125} radix; 126 127 128/* Mapping of radix and binary class to length. */ 129static const int length_map[2][3] = 130{ 131 [ELFCLASS32 - 1] = 132 { 133 [radix_hex] = 8, 134 [radix_decimal] = 10, 135 [radix_octal] = 11 136 }, 137 [ELFCLASS64 - 1] = 138 { 139 [radix_hex] = 16, 140 [radix_decimal] = 20, 141 [radix_octal] = 22 142 } 143}; 144 145/* True if total sizes should be printed. */ 146static bool totals; 147/* To print the total sizes in a reasonable format remember the highest 148 "class" of ELF binaries processed. */ 149static int totals_class; 150 151 152int 153main (int argc, char *argv[]) 154{ 155 int remaining; 156 int result = 0; 157 158 /* We use no threads here which can interfere with handling a stream. */ 159 __fsetlocking (stdin, FSETLOCKING_BYCALLER); 160 __fsetlocking (stdout, FSETLOCKING_BYCALLER); 161 __fsetlocking (stderr, FSETLOCKING_BYCALLER); 162 163 /* Set locale. */ 164 setlocale (LC_ALL, ""); 165 166 /* Make sure the message catalog can be found. */ 167 bindtextdomain (PACKAGE_TARNAME, LOCALEDIR); 168 169 /* Initialize the message catalog. */ 170 textdomain (PACKAGE_TARNAME); 171 172 /* Parse and process arguments. */ 173 argp_parse (&argp, argc, argv, 0, &remaining, NULL); 174 175 176 /* Tell the library which version we are expecting. */ 177 elf_version (EV_CURRENT); 178 179 if (remaining == argc) 180 /* The user didn't specify a name so we use a.out. */ 181 result = process_file ("a.out"); 182 else 183 /* Process all the remaining files. */ 184 do 185 result |= process_file (argv[remaining]); 186 while (++remaining < argc); 187 188 /* Print the total sizes but only if the output format is BSD and at 189 least one file has been correctly read (i.e., we recognized the 190 class). */ 191 if (totals && format == format_bsd && totals_class != 0) 192 show_bsd_totals (); 193 194 return result; 195} 196 197 198/* Handle program arguments. */ 199static error_t 200parse_opt (int key, char *arg, 201 struct argp_state *state __attribute__ ((unused))) 202{ 203 switch (key) 204 { 205 case 'd': 206 radix = radix_decimal; 207 break; 208 209 case 'f': 210 format = format_sysv_one_line; 211 break; 212 213 case 'o': 214 radix = radix_octal; 215 break; 216 217 case 'x': 218 radix = radix_hex; 219 break; 220 221 case 'A': 222 format = format_sysv; 223 break; 224 225 case 'B': 226 format = format_bsd; 227 break; 228 229 case 'F': 230 format = format_segments; 231 break; 232 233 case OPT_FORMAT: 234 if (strcmp (arg, "bsd") == 0 || strcmp (arg, "berkeley") == 0) 235 format = format_bsd; 236 else if (likely (strcmp (arg, "sysv") == 0)) 237 format = format_sysv; 238 else 239 error_exit (0, _("Invalid format: %s"), arg); 240 break; 241 242 case OPT_RADIX: 243 if (strcmp (arg, "x") == 0 || strcmp (arg, "16") == 0) 244 radix = radix_hex; 245 else if (strcmp (arg, "d") == 0 || strcmp (arg, "10") == 0) 246 radix = radix_decimal; 247 else if (strcmp (arg, "o") == 0 || strcmp (arg, "8") == 0) 248 radix = radix_octal; 249 else 250 error_exit (0, _("Invalid radix: %s"), arg); 251 break; 252 253 case 't': 254 totals = true; 255 break; 256 257 default: 258 return ARGP_ERR_UNKNOWN; 259 } 260 return 0; 261} 262 263 264/* Open the file and determine the type. */ 265static int 266process_file (const char *fname) 267{ 268 int fd = open (fname, O_RDONLY); 269 if (unlikely (fd == -1)) 270 { 271 error (0, errno, _("cannot open '%s'"), fname); 272 return 1; 273 } 274 275 /* Now get the ELF descriptor. */ 276 Elf *elf = elf_begin (fd, ELF_C_READ_MMAP, NULL); 277 if (likely (elf != NULL)) 278 { 279 if (elf_kind (elf) == ELF_K_ELF) 280 { 281 handle_elf (elf, NULL, fname); 282 283 if (unlikely (elf_end (elf) != 0)) 284 INTERNAL_ERROR (fname); 285 286 if (unlikely (close (fd) != 0)) 287 error_exit (errno, _("while closing '%s'"), fname); 288 289 return 0; 290 } 291 else if (likely (elf_kind (elf) == ELF_K_AR)) 292 { 293 int result = handle_ar (fd, elf, NULL, fname); 294 295 if (unlikely (close (fd) != 0)) 296 error_exit (errno, _("while closing '%s'"), fname); 297 298 return result; 299 } 300 301 /* We cannot handle this type. Close the descriptor anyway. */ 302 if (unlikely (elf_end (elf) != 0)) 303 INTERNAL_ERROR (fname); 304 } 305 306 if (unlikely (close (fd) != 0)) 307 error_exit (errno, _("while closing '%s'"), fname); 308 309 error (0, 0, _("%s: file format not recognized"), fname); 310 311 return 1; 312} 313 314 315/* Print the BSD-style header. This is done exactly once. */ 316static void 317print_header (Elf *elf) 318{ 319 static int done; 320 321 if (! done) 322 { 323 int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal]; 324 int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex]; 325 326 printf ("%*s %*s %*s %*s %*s %s\n", 327 ddigits - 2, sgettext ("bsd|text"), 328 ddigits - 2, sgettext ("bsd|data"), 329 ddigits - 2, sgettext ("bsd|bss"), 330 ddigits - 2, sgettext ("bsd|dec"), 331 xdigits - 2, sgettext ("bsd|hex"), 332 sgettext ("bsd|filename")); 333 334 done = 1; 335 } 336} 337 338 339static int 340handle_ar (int fd, Elf *elf, const char *prefix, const char *fname) 341{ 342 size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); 343 size_t fname_len = strlen (fname) + 1; 344 char new_prefix[prefix_len + 1 + fname_len]; 345 char *cp = new_prefix; 346 347 /* Create the full name of the file. */ 348 if (prefix != NULL) 349 { 350 cp = mempcpy (cp, prefix, prefix_len); 351 *cp++ = ':'; 352 } 353 memcpy (cp, fname, fname_len); 354 355 /* Process all the files contained in the archive. */ 356 int result = 0; 357 Elf *subelf; 358 Elf_Cmd cmd = ELF_C_READ_MMAP; 359 while ((subelf = elf_begin (fd, cmd, elf)) != NULL) 360 { 361 /* The the header for this element. */ 362 Elf_Arhdr *arhdr = elf_getarhdr (subelf); 363 364 if (elf_kind (subelf) == ELF_K_ELF) 365 handle_elf (subelf, new_prefix, arhdr->ar_name); 366 else if (likely (elf_kind (subelf) == ELF_K_AR)) 367 result |= handle_ar (fd, subelf, new_prefix, arhdr->ar_name); 368 /* else signal error??? */ 369 370 /* Get next archive element. */ 371 cmd = elf_next (subelf); 372 if (unlikely (elf_end (subelf) != 0)) 373 INTERNAL_ERROR (fname); 374 } 375 376 /* Only close ELF handle if this was a "top level" ar file. */ 377 if (prefix == NULL) 378 if (unlikely (elf_end (elf) != 0)) 379 INTERNAL_ERROR (fname); 380 381 return result; 382} 383 384 385/* Show sizes in SysV format. */ 386static void 387show_sysv (Elf *elf, const char *prefix, const char *fname, 388 const char *fullname) 389{ 390 int maxlen = 10; 391 const int digits = length_map[gelf_getclass (elf) - 1][radix]; 392 393 /* Get the section header string table index. */ 394 size_t shstrndx; 395 if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0)) 396 error_exit (0, _("cannot get section header string table index")); 397 398 /* First round over the sections: determine the longest section name. */ 399 Elf_Scn *scn = NULL; 400 while ((scn = elf_nextscn (elf, scn)) != NULL) 401 { 402 GElf_Shdr shdr_mem; 403 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 404 405 if (shdr == NULL) 406 INTERNAL_ERROR (fullname); 407 408 /* Ignore all sections which are not used at runtime. */ 409 const char *name = elf_strptr (elf, shstrndx, shdr->sh_name); 410 if (name != NULL && (shdr->sh_flags & SHF_ALLOC) != 0) 411 maxlen = MAX (maxlen, (int) strlen (name)); 412 } 413 414 fputs_unlocked (fname, stdout); 415 if (prefix != NULL) 416 printf (_(" (ex %s)"), prefix); 417 printf (":\n%-*s %*s %*s\n", 418 maxlen, sgettext ("sysv|section"), 419 digits - 2, sgettext ("sysv|size"), 420 digits, sgettext ("sysv|addr")); 421 422 /* Iterate over all sections. */ 423 GElf_Off total = 0; 424 while ((scn = elf_nextscn (elf, scn)) != NULL) 425 { 426 GElf_Shdr shdr_mem; 427 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 428 429 if (shdr == NULL) 430 INTERNAL_ERROR (fullname); 431 432 /* Ignore all sections which are not used at runtime. */ 433 if ((shdr->sh_flags & SHF_ALLOC) != 0) 434 { 435 printf ((radix == radix_hex 436 ? "%-*s %*" PRIx64 " %*" PRIx64 "\n" 437 : (radix == radix_decimal 438 ? "%-*s %*" PRId64 " %*" PRId64 "\n" 439 : "%-*s %*" PRIo64 " %*" PRIo64 "\n")), 440 maxlen, elf_strptr (elf, shstrndx, shdr->sh_name), 441 digits - 2, shdr->sh_size, 442 digits, shdr->sh_addr); 443 444 total += shdr->sh_size; 445 } 446 } 447 448 if (radix == radix_hex) 449 printf ("%-*s %*" PRIx64 "\n\n\n", maxlen, sgettext ("sysv|Total"), 450 digits - 2, total); 451 else if (radix == radix_decimal) 452 printf ("%-*s %*" PRId64 "\n\n\n", maxlen, sgettext ("sysv|Total"), 453 digits - 2, total); 454 else 455 printf ("%-*s %*" PRIo64 "\n\n\n", maxlen, sgettext ("sysv|Total"), 456 digits - 2, total); 457} 458 459 460/* Show sizes in SysV format in one line. */ 461static void 462show_sysv_one_line (Elf *elf) 463{ 464 /* Get the section header string table index. */ 465 size_t shstrndx; 466 if (unlikely (elf_getshdrstrndx (elf, &shstrndx) < 0)) 467 error_exit (0, _("cannot get section header string table index")); 468 469 /* Iterate over all sections. */ 470 GElf_Off total = 0; 471 bool first = true; 472 Elf_Scn *scn = NULL; 473 while ((scn = elf_nextscn (elf, scn)) != NULL) 474 { 475 GElf_Shdr shdr_mem; 476 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 477 478 if (unlikely (shdr == NULL)) 479 error_exit (0, _("cannot get section header")); 480 481 /* Ignore all sections which are not used at runtime. */ 482 if ((shdr->sh_flags & SHF_ALLOC) == 0) 483 continue; 484 485 if (! first) 486 fputs_unlocked (" + ", stdout); 487 first = false; 488 489 printf ((radix == radix_hex ? "%" PRIx64 "(%s)" 490 : (radix == radix_decimal ? "%" PRId64 "(%s)" 491 : "%" PRIo64 "(%s)")), 492 shdr->sh_size, elf_strptr (elf, shstrndx, shdr->sh_name)); 493 494 total += shdr->sh_size; 495 } 496 497 if (radix == radix_hex) 498 printf (" = %#" PRIx64 "\n", total); 499 else if (radix == radix_decimal) 500 printf (" = %" PRId64 "\n", total); 501 else 502 printf (" = %" PRIo64 "\n", total); 503} 504 505 506/* Variables to add up the sizes of all files. */ 507static uintmax_t total_textsize; 508static uintmax_t total_datasize; 509static uintmax_t total_bsssize; 510 511 512/* Show sizes in BSD format. */ 513static void 514show_bsd (Elf *elf, const char *prefix, const char *fname, 515 const char *fullname) 516{ 517 GElf_Off textsize = 0; 518 GElf_Off datasize = 0; 519 GElf_Off bsssize = 0; 520 const int ddigits = length_map[gelf_getclass (elf) - 1][radix_decimal]; 521 const int xdigits = length_map[gelf_getclass (elf) - 1][radix_hex]; 522 523 /* Iterate over all sections. */ 524 Elf_Scn *scn = NULL; 525 while ((scn = elf_nextscn (elf, scn)) != NULL) 526 { 527 GElf_Shdr shdr_mem; 528 GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 529 530 if (shdr == NULL) 531 INTERNAL_ERROR (fullname); 532 533 /* Ignore all sections which are not marked as loaded. */ 534 if ((shdr->sh_flags & SHF_ALLOC) == 0) 535 continue; 536 537 if ((shdr->sh_flags & SHF_WRITE) == 0) 538 textsize += shdr->sh_size; 539 else if (shdr->sh_type == SHT_NOBITS) 540 bsssize += shdr->sh_size; 541 else 542 datasize += shdr->sh_size; 543 } 544 545 printf (radix == radix_decimal 546 ? "%*" PRId64 " %*" PRId64 " %*" PRId64 " %*" PRId64 " %*" PRIx64 " %s" 547 : radix == radix_hex 548 ? "%#*" PRIx64 " %#*" PRIx64 " %#*" PRIx64 " %*" PRId64 " %*" PRIx64 " %s" 549 : "%#*" PRIo64 " %#*" PRIo64 " %#*" PRIo64 " %*" PRId64 " %*" PRIx64 " %s", 550 ddigits - 2, textsize, 551 ddigits - 2, datasize, 552 ddigits - 2, bsssize, 553 ddigits - 2, textsize + datasize + bsssize, 554 xdigits - 2, textsize + datasize + bsssize, 555 fname); 556 if (prefix != NULL) 557 printf (_(" (ex %s)"), prefix); 558 fputs_unlocked ("\n", stdout); 559 560 total_textsize += textsize; 561 total_datasize += datasize; 562 total_bsssize += bsssize; 563 564 totals_class = MAX (totals_class, gelf_getclass (elf)); 565} 566 567 568/* Show total size. */ 569static void 570show_bsd_totals (void) 571{ 572 int ddigits = length_map[totals_class - 1][radix_decimal]; 573 int xdigits = length_map[totals_class - 1][radix_hex]; 574 575 printf ("%*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" PRIuMAX " %*" 576 PRIxMAX " %s", 577 ddigits - 2, total_textsize, 578 ddigits - 2, total_datasize, 579 ddigits - 2, total_bsssize, 580 ddigits - 2, total_textsize + total_datasize + total_bsssize, 581 xdigits - 2, total_textsize + total_datasize + total_bsssize, 582 _("(TOTALS)\n")); 583} 584 585 586/* Show size and permission of loadable segments. */ 587static void 588show_segments (Elf *elf, const char *fullname) 589{ 590 size_t phnum; 591 if (elf_getphdrnum (elf, &phnum) != 0) 592 INTERNAL_ERROR (fullname); 593 594 GElf_Off total = 0; 595 bool first = true; 596 for (size_t cnt = 0; cnt < phnum; ++cnt) 597 { 598 GElf_Phdr phdr_mem; 599 GElf_Phdr *phdr; 600 601 phdr = gelf_getphdr (elf, cnt, &phdr_mem); 602 if (phdr == NULL) 603 INTERNAL_ERROR (fullname); 604 605 if (phdr->p_type != PT_LOAD) 606 /* Only load segments. */ 607 continue; 608 609 if (! first) 610 fputs_unlocked (" + ", stdout); 611 first = false; 612 613 printf (radix == radix_hex ? "%" PRIx64 "(%c%c%c)" 614 : (radix == radix_decimal ? "%" PRId64 "(%c%c%c)" 615 : "%" PRIo64 "(%c%c%c)"), 616 phdr->p_memsz, 617 (phdr->p_flags & PF_R) == 0 ? '-' : 'r', 618 (phdr->p_flags & PF_W) == 0 ? '-' : 'w', 619 (phdr->p_flags & PF_X) == 0 ? '-' : 'x'); 620 621 total += phdr->p_memsz; 622 } 623 624 if (radix == radix_hex) 625 printf (" = %#" PRIx64 "\n", total); 626 else if (radix == radix_decimal) 627 printf (" = %" PRId64 "\n", total); 628 else 629 printf (" = %" PRIo64 "\n", total); 630} 631 632 633static void 634handle_elf (Elf *elf, const char *prefix, const char *fname) 635{ 636 size_t prefix_len = prefix == NULL ? 0 : strlen (prefix); 637 size_t fname_len = strlen (fname) + 1; 638 char fullname[prefix_len + 1 + fname_len]; 639 char *cp = fullname; 640 641 /* Create the full name of the file. */ 642 if (prefix != NULL) 643 { 644 cp = mempcpy (cp, prefix, prefix_len); 645 *cp++ = ':'; 646 } 647 memcpy (cp, fname, fname_len); 648 649 if (format == format_sysv) 650 show_sysv (elf, prefix, fname, fullname); 651 else if (format == format_sysv_one_line) 652 show_sysv_one_line (elf); 653 else if (format == format_segments) 654 show_segments (elf, fullname); 655 else 656 { 657 print_header (elf); 658 659 show_bsd (elf, prefix, fname, fullname); 660 } 661} 662 663 664#include "debugpred.h" 665