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
45ELFTC_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
51enum return_code {
52	RETURN_OK,
53	RETURN_DATAERR,
54	RETURN_USAGE
55};
56
57enum output_style {
58	STYLE_BERKELEY,
59	STYLE_SYSV
60};
61
62enum radix_style {
63	RADIX_OCTAL,
64	RADIX_DECIMAL,
65	RADIX_HEX
66};
67
68static uint64_t bss_size, data_size, text_size, total_size;
69static uint64_t bss_size_total, data_size_total, text_size_total;
70static int show_totals;
71static int size_option;
72static enum radix_style radix = RADIX_DECIMAL;
73static enum output_style style = STYLE_BERKELEY;
74
75static struct {
76	int row;
77	int col;
78	int *width;
79	char ***tbl;
80} *tb;
81
82enum {
83	OPT_FORMAT,
84	OPT_RADIX
85};
86
87static 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
96static void	berkeley_calc(GElf_Shdr *);
97static void	berkeley_footer(const char *, const char *, const char *);
98static void	berkeley_header(void);
99static void	berkeley_totals(void);
100static int	handle_core(char const *, Elf *elf, GElf_Ehdr *);
101static void	handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **);
102static int	handle_elf(int, char const *);
103static void	handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t,
104		    const char *);
105static void	show_version(void);
106static void	sysv_header(const char *, Elf_Arhdr *);
107static void	sysv_footer(void);
108static void	sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *);
109static void	usage(void);
110static void	tbl_new(int);
111static void	tbl_print(const char *, int);
112static void	tbl_print_num(uint64_t, enum radix_style, int);
113static void	tbl_append(void);
114static 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 */
121int
122main(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
243static int
244xlatetom(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 */
291static void
292handle_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 */
479static void
480handle_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 */
530static int
531handle_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 */
614static int
615handle_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 */
688static void
689sysv_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
704static void
705sysv_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
722static void
723sysv_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 */
735static void
736berkeley_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
757static void
758berkeley_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
776static void
777berkeley_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
793static void
794berkeley_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
825static void
826tbl_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
841static void
842tbl_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
855static void
856tbl_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
865static void
866tbl_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
880static void
881tbl_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	"\
922Usage: %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
941static void
942usage(void)
943{
944	(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
945	exit(EXIT_FAILURE);
946}
947
948static void
949show_version(void)
950{
951	(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
952	exit(EXIT_SUCCESS);
953}
954