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