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
main(int argc,char ** argv)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 *
xlatetom(Elf * elf,GElf_Ehdr * elfhdr,void * _src,void * _dst,Elf_Type type,size_t size)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
handle_core_note(Elf * elf,GElf_Ehdr * elfhdr,GElf_Phdr * phdr,char ** cmd_line)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
handle_phdr(Elf * elf,GElf_Ehdr * elfhdr,GElf_Phdr * phdr,uint32_t idx,const char * name)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
handle_core(char const * name,Elf * elf,GElf_Ehdr * elfhdr)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
handle_elf(char const * name)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
sysv_header(const char * name,Elf_Arhdr * arhdr)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
sysv_calc(Elf * elf,GElf_Ehdr * elfhdr,GElf_Shdr * shdr)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
sysv_footer(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
berkeley_header(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
berkeley_calc(GElf_Shdr * shdr)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
berkeley_totals(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
berkeley_footer(const char * name,const char * ar_name,const char * msg)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
tbl_new(int col)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
tbl_print(const char * s,int col)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
tbl_print_num(uint64_t num,enum radix_style rad,int col)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
tbl_append(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
tbl_flush(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
usage(void)918 usage(void)
919 {
920 (void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
921 exit(EXIT_FAILURE);
922 }
923
924 static void
show_version(void)925 show_version(void)
926 {
927 (void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
928 exit(EXIT_SUCCESS);
929 }
930