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