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