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
41ELFTC_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
47enum return_code {
48	RETURN_OK,
49	RETURN_NOINPUT,
50	RETURN_DATAERR,
51	RETURN_USAGE
52};
53
54enum output_style {
55	STYLE_BERKELEY,
56	STYLE_SYSV
57};
58
59enum radix_style {
60	RADIX_OCTAL,
61	RADIX_DECIMAL,
62	RADIX_HEX
63};
64
65static uint64_t bss_size, data_size, text_size, total_size;
66static uint64_t bss_size_total, data_size_total, text_size_total;
67static int show_totals;
68static int size_option;
69static enum radix_style radix = RADIX_DECIMAL;
70static enum output_style style = STYLE_BERKELEY;
71static const char *default_args[2] = { "a.out", NULL };
72
73static struct {
74	int row;
75	int col;
76	int *width;
77	char ***tbl;
78} *tb;
79
80enum {
81	OPT_FORMAT,
82	OPT_RADIX
83};
84
85static 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
94static void	berkeley_calc(GElf_Shdr *);
95static void	berkeley_footer(const char *, const char *, const char *);
96static void	berkeley_header(void);
97static void	berkeley_totals(void);
98static int	handle_core(char const *, Elf *elf, GElf_Ehdr *);
99static void	handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **);
100static int	handle_elf(char const *);
101static void	handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t,
102		    const char *);
103static void	show_version(void);
104static void	sysv_header(const char *, Elf_Arhdr *);
105static void	sysv_footer(void);
106static void	sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *);
107static void	usage(void);
108static void	tbl_new(int);
109static void	tbl_print(const char *, int);
110static void	tbl_print_num(uint64_t, enum radix_style, int);
111static void	tbl_append(void);
112static 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 */
119int
120main(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
214static int
215xlatetom(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	    NULL ? 0 : 1);
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 */
262static void
263handle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
264    char **cmd_line)
265{
266	size_t max_size, segment_end;
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	if (offset >= max_size || phdr->p_filesz > max_size - offset) {
281		warnx("invalid PHDR offset");
282		return;
283	}
284	segment_end = phdr->p_offset + phdr->p_filesz;
285
286	while (data != NULL && offset + sizeof(Elf32_Nhdr) < segment_end) {
287		nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset);
288		memset(&nhdr_l, 0, sizeof(Elf32_Nhdr));
289		if (xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type,
290		    ELF_T_WORD, sizeof(Elf32_Word)) != 0 ||
291		    xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz,
292		    ELF_T_WORD, sizeof(Elf32_Word)) != 0 ||
293		    xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz,
294		    ELF_T_WORD, sizeof(Elf32_Word)) != 0)
295			break;
296
297		if (offset + sizeof(Elf32_Nhdr) +
298		    ELF_ALIGN(nhdr_l.n_namesz, 4) +
299		    ELF_ALIGN(nhdr_l.n_descsz, 4) >= segment_end) {
300			warnx("invalid note header");
301			return;
302		}
303
304		name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr));
305		switch (nhdr_l.n_type) {
306		case NT_PRSTATUS: {
307			raw_size = 0;
308			if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD &&
309			    nhdr_l.n_namesz == 0x8 &&
310			    !strcmp(name,"FreeBSD")) {
311				if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) {
312					raw_size = (uint64_t)*((uint32_t *)
313					    (uintptr_t)(name +
314						ELF_ALIGN((int32_t)
315						nhdr_l.n_namesz, 4) + 8));
316					ver = (uintptr_t)NOTE_OFFSET_32(nhdr,
317					    nhdr_l.n_namesz,0);
318					if (*((int *)ver) == 1)
319						pid = PID32(nhdr,
320						    nhdr_l.n_namesz, 24);
321				} else {
322					raw_size = *((uint64_t *)(uintptr_t)
323					    (name + ELF_ALIGN((int32_t)
324						nhdr_l.n_namesz, 8) + 16));
325					ver = (uintptr_t)NOTE_OFFSET_64(nhdr,
326					    nhdr_l.n_namesz,0);
327					if (*((int *)ver) == 1)
328						pid = PID64(nhdr,
329						    nhdr_l.n_namesz, 40);
330				}
331				(void)xlatetom(elf, elfhdr, &raw_size,
332				    &raw_size, ELF_T_WORD, sizeof(uint64_t));
333				(void)xlatetom(elf, elfhdr, &pid, &pid,
334				    ELF_T_WORD, sizeof(pid_t));
335			}
336
337			if (raw_size != 0 && style == STYLE_SYSV) {
338				(void) snprintf(buf, BUF_SIZE, "%s/%d",
339				    ".reg", pid);
340				tbl_append();
341				tbl_print(buf, 0);
342				tbl_print_num(raw_size, radix, 1);
343				tbl_print_num(0, radix, 2);
344				if (!reg_pseudo) {
345					tbl_append();
346					tbl_print(".reg", 0);
347					tbl_print_num(raw_size, radix, 1);
348					tbl_print_num(0, radix, 2);
349					reg_pseudo = 1;
350					text_size_total += raw_size;
351				}
352				text_size_total += raw_size;
353			}
354		}
355		break;
356		case NT_FPREGSET:	/* same as NT_PRFPREG */
357			if (style == STYLE_SYSV) {
358				(void) snprintf(buf, BUF_SIZE,
359				    "%s/%d", ".reg2", pid);
360				tbl_append();
361				tbl_print(buf, 0);
362				tbl_print_num(nhdr_l.n_descsz, radix, 1);
363				tbl_print_num(0, radix, 2);
364				if (!reg2_pseudo) {
365					tbl_append();
366					tbl_print(".reg2", 0);
367					tbl_print_num(nhdr_l.n_descsz, radix,
368					    1);
369					tbl_print_num(0, radix, 2);
370					reg2_pseudo = 1;
371					text_size_total += nhdr_l.n_descsz;
372				}
373				text_size_total += nhdr_l.n_descsz;
374			}
375			break;
376#if 0
377		case NT_AUXV:
378			if (style == STYLE_SYSV) {
379				tbl_append();
380				tbl_print(".auxv", 0);
381				tbl_print_num(nhdr_l.n_descsz, radix, 1);
382				tbl_print_num(0, radix, 2);
383				text_size_total += nhdr_l.n_descsz;
384			}
385			break;
386		case NT_PRXFPREG:
387			if (style == STYLE_SYSV) {
388				(void) snprintf(buf, BUF_SIZE, "%s/%d",
389				    ".reg-xfp", pid);
390				tbl_append();
391				tbl_print(buf, 0);
392				tbl_print_num(nhdr_l.n_descsz, radix, 1);
393				tbl_print_num(0, radix, 2);
394				if (!regxfp_pseudo) {
395					tbl_append();
396					tbl_print(".reg-xfp", 0);
397					tbl_print_num(nhdr_l.n_descsz, radix,
398					    1);
399					tbl_print_num(0, radix, 2);
400					regxfp_pseudo = 1;
401					text_size_total += nhdr_l.n_descsz;
402				}
403				text_size_total += nhdr_l.n_descsz;
404			}
405			break;
406		case NT_PSINFO:
407#endif
408		case NT_PRPSINFO: {
409			/* FreeBSD 64-bit */
410			if (nhdr_l.n_descsz == 0x78 &&
411				!strcmp(name,"FreeBSD")) {
412				*cmd_line = strdup(NOTE_OFFSET_64(nhdr,
413				    nhdr_l.n_namesz, 33));
414			/* FreeBSD 32-bit */
415			} else if (nhdr_l.n_descsz == 0x6c &&
416				!strcmp(name,"FreeBSD")) {
417				*cmd_line = strdup(NOTE_OFFSET_32(nhdr,
418				    nhdr_l.n_namesz, 25));
419			}
420			/* Strip any trailing spaces */
421			if (*cmd_line != NULL) {
422				char *s;
423
424				s = *cmd_line + strlen(*cmd_line);
425				while (s > *cmd_line) {
426					if (*(s-1) != 0x20) break;
427					s--;
428				}
429				*s = 0;
430			}
431			break;
432		}
433#if 0
434		case NT_PSTATUS:
435		case NT_LWPSTATUS:
436#endif
437		default:
438			break;
439		}
440		NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset);
441	}
442}
443
444/*
445 * Handles program headers except for PT_NOTE, when sysv output style is
446 * chosen, prints out the segment name and length. For berkely output
447 * style only PT_LOAD segments are handled, and text,
448 * data, bss size is calculated for them.
449 */
450static void
451handle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
452    uint32_t idx, const char *name)
453{
454	uint64_t addr, size;
455	int split;
456	char buf[BUF_SIZE];
457
458	if (elf == NULL || elfhdr == NULL || phdr == NULL)
459		return;
460
461	split = (phdr->p_memsz > 0) && 	(phdr->p_filesz > 0) &&
462	    (phdr->p_memsz > phdr->p_filesz);
463
464	if (style == STYLE_SYSV) {
465		(void) snprintf(buf, BUF_SIZE,
466		    "%s%d%s", name, idx, (split ? "a" : ""));
467		tbl_append();
468		tbl_print(buf, 0);
469		tbl_print_num(phdr->p_filesz, radix, 1);
470		tbl_print_num(phdr->p_vaddr, radix, 2);
471		text_size_total += phdr->p_filesz;
472		if (split) {
473			size = phdr->p_memsz - phdr->p_filesz;
474			addr = phdr->p_vaddr + phdr->p_filesz;
475			(void) snprintf(buf, BUF_SIZE, "%s%d%s", name,
476			    idx, "b");
477			text_size_total += phdr->p_memsz - phdr->p_filesz;
478			tbl_append();
479			tbl_print(buf, 0);
480			tbl_print_num(size, radix, 1);
481			tbl_print_num(addr, radix, 2);
482		}
483	} else {
484		if (phdr->p_type != PT_LOAD)
485			return;
486		if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) {
487			data_size += phdr->p_filesz;
488			if (split)
489				data_size += phdr->p_memsz - phdr->p_filesz;
490		} else {
491			text_size += phdr->p_filesz;
492			if (split)
493				text_size += phdr->p_memsz - phdr->p_filesz;
494		}
495	}
496}
497
498/*
499 * Given a core dump file, this function maps program headers to segments.
500 */
501static int
502handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr)
503{
504	GElf_Phdr phdr;
505	uint32_t i;
506	char *core_cmdline;
507	const char *seg_name;
508
509	if (name == NULL || elf == NULL || elfhdr == NULL)
510		return (RETURN_DATAERR);
511	if  (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE)
512		return (RETURN_DATAERR);
513
514	seg_name = core_cmdline = NULL;
515	if (style == STYLE_SYSV)
516		sysv_header(name, NULL);
517	else
518		berkeley_header();
519
520	for (i = 0; i < elfhdr->e_phnum; i++) {
521		if (gelf_getphdr(elf, i, &phdr) != NULL) {
522			if (phdr.p_type == PT_NOTE) {
523				handle_phdr(elf, elfhdr, &phdr, i, "note");
524				handle_core_note(elf, elfhdr, &phdr,
525				    &core_cmdline);
526			} else {
527				switch(phdr.p_type) {
528				case PT_NULL:
529					seg_name = "null";
530					break;
531				case PT_LOAD:
532					seg_name = "load";
533					break;
534				case PT_DYNAMIC:
535					seg_name = "dynamic";
536					break;
537				case PT_INTERP:
538					seg_name = "interp";
539					break;
540				case PT_SHLIB:
541					seg_name = "shlib";
542					break;
543				case PT_PHDR:
544					seg_name = "phdr";
545					break;
546				case PT_GNU_EH_FRAME:
547					seg_name = "eh_frame_hdr";
548					break;
549				case PT_GNU_STACK:
550					seg_name = "stack";
551					break;
552				default:
553					seg_name = "segment";
554				}
555				handle_phdr(elf, elfhdr, &phdr, i, seg_name);
556			}
557		}
558	}
559
560	if (style == STYLE_BERKELEY) {
561		if (core_cmdline != NULL) {
562			berkeley_footer(core_cmdline, name,
563			    "core file invoked as");
564		} else {
565			berkeley_footer(core_cmdline, name, "core file");
566		}
567	} else {
568		sysv_footer();
569		if (core_cmdline != NULL) {
570			(void) printf(" (core file invoked as %s)\n\n",
571			    core_cmdline);
572		} else {
573			(void) printf(" (core file)\n\n");
574		}
575	}
576	free(core_cmdline);
577	return (RETURN_OK);
578}
579
580/*
581 * Given an elf object,ar(1) filename, and based on the output style
582 * and radix format the various sections and their length will be printed
583 * or the size of the text, data, bss sections will be printed out.
584 */
585static int
586handle_elf(char const *name)
587{
588	GElf_Ehdr elfhdr;
589	GElf_Shdr shdr;
590	Elf *elf, *elf1;
591	Elf_Arhdr *arhdr;
592	Elf_Scn *scn;
593	Elf_Cmd elf_cmd;
594	int exit_code, fd;
595
596	if (name == NULL)
597		return (RETURN_NOINPUT);
598
599	if ((fd = open(name, O_RDONLY, 0)) < 0)
600		return (RETURN_NOINPUT);
601
602	elf_cmd = ELF_C_READ;
603	elf1 = elf_begin(fd, elf_cmd, NULL);
604	while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) {
605		arhdr = elf_getarhdr(elf);
606		if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) {
607			(void) elf_end(elf);
608			(void) elf_end(elf1);
609			(void) close(fd);
610			return (RETURN_DATAERR);
611		}
612		if (elf_kind(elf) != ELF_K_ELF ||
613		    (gelf_getehdr(elf, &elfhdr) == NULL)) {
614			elf_cmd = elf_next(elf);
615			(void) elf_end(elf);
616			warnx("%s: File format not recognized",
617			    arhdr != NULL ? arhdr->ar_name : name);
618			continue;
619		}
620		/* Core dumps are handled separately */
621		if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) {
622			exit_code = handle_core(name, elf, &elfhdr);
623			(void) elf_end(elf);
624			(void) elf_end(elf1);
625			(void) close(fd);
626			return (exit_code);
627		} else {
628			scn = NULL;
629			if (style == STYLE_BERKELEY) {
630				berkeley_header();
631				while ((scn = elf_nextscn(elf, scn)) != NULL) {
632					if (gelf_getshdr(scn, &shdr) != NULL)
633						berkeley_calc(&shdr);
634				}
635			} else {
636				sysv_header(name, arhdr);
637				scn = NULL;
638				while ((scn = elf_nextscn(elf, scn)) != NULL) {
639					if (gelf_getshdr(scn, &shdr) !=	NULL)
640						sysv_calc(elf, &elfhdr, &shdr);
641				}
642			}
643			if (style == STYLE_BERKELEY) {
644				if (arhdr != NULL) {
645					berkeley_footer(name, arhdr->ar_name,
646					    "ex");
647				} else {
648					berkeley_footer(name, NULL, "ex");
649				}
650			} else {
651				sysv_footer();
652			}
653		}
654		elf_cmd = elf_next(elf);
655		(void) elf_end(elf);
656	}
657	(void) elf_end(elf1);
658	(void) close(fd);
659	return (RETURN_OK);
660}
661
662/*
663 * Sysv formatting helper functions.
664 */
665static void
666sysv_header(const char *name, Elf_Arhdr *arhdr)
667{
668
669	text_size_total = 0;
670	if (arhdr != NULL)
671		(void) printf("%s   (ex %s):\n", arhdr->ar_name, name);
672	else
673		(void) printf("%s  :\n", name);
674	tbl_new(3);
675	tbl_append();
676	tbl_print("section", 0);
677	tbl_print("size", 1);
678	tbl_print("addr", 2);
679}
680
681static void
682sysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr)
683{
684	char *section_name;
685
686	section_name = elf_strptr(elf, elfhdr->e_shstrndx,
687	    (size_t) shdr->sh_name);
688	if ((shdr->sh_type == SHT_SYMTAB ||
689	    shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA ||
690	    shdr->sh_type == SHT_REL) && shdr->sh_addr == 0)
691		return;
692	tbl_append();
693	tbl_print(section_name, 0);
694	tbl_print_num(shdr->sh_size, radix, 1);
695	tbl_print_num(shdr->sh_addr, radix, 2);
696	text_size_total += shdr->sh_size;
697}
698
699static void
700sysv_footer(void)
701{
702	tbl_append();
703	tbl_print("Total", 0);
704	tbl_print_num(text_size_total, radix, 1);
705	tbl_flush();
706	putchar('\n');
707}
708
709/*
710 * berkeley style output formatting helper functions.
711 */
712static void
713berkeley_header(void)
714{
715	static int printed;
716
717	text_size = data_size = bss_size = 0;
718	if (!printed) {
719		tbl_new(6);
720		tbl_append();
721		tbl_print("text", 0);
722		tbl_print("data", 1);
723		tbl_print("bss", 2);
724		if (radix == RADIX_OCTAL)
725			tbl_print("oct", 3);
726		else
727			tbl_print("dec", 3);
728		tbl_print("hex", 4);
729		tbl_print("filename", 5);
730		printed = 1;
731	}
732}
733
734static void
735berkeley_calc(GElf_Shdr *shdr)
736{
737	if (shdr != NULL) {
738		if (!(shdr->sh_flags & SHF_ALLOC))
739			return;
740		if ((shdr->sh_flags & SHF_ALLOC) &&
741		    ((shdr->sh_flags & SHF_EXECINSTR) ||
742		    !(shdr->sh_flags & SHF_WRITE)))
743			text_size += shdr->sh_size;
744		else if ((shdr->sh_flags & SHF_ALLOC) &&
745		    (shdr->sh_flags & SHF_WRITE) &&
746		    (shdr->sh_type != SHT_NOBITS))
747			data_size += shdr->sh_size;
748		else
749			bss_size += shdr->sh_size;
750	}
751}
752
753static void
754berkeley_totals(void)
755{
756	uint64_t grand_total;
757
758	grand_total = text_size_total + data_size_total + bss_size_total;
759	tbl_append();
760	tbl_print_num(text_size_total, radix, 0);
761	tbl_print_num(data_size_total, radix, 1);
762	tbl_print_num(bss_size_total, radix, 2);
763	if (radix == RADIX_OCTAL)
764		tbl_print_num(grand_total, RADIX_OCTAL, 3);
765	else
766		tbl_print_num(grand_total, RADIX_DECIMAL, 3);
767	tbl_print_num(grand_total, RADIX_HEX, 4);
768}
769
770static void
771berkeley_footer(const char *name, const char *ar_name, const char *msg)
772{
773	char buf[BUF_SIZE];
774
775	total_size = text_size + data_size + bss_size;
776	if (show_totals) {
777		text_size_total += text_size;
778		bss_size_total += bss_size;
779		data_size_total += data_size;
780	}
781
782	tbl_append();
783	tbl_print_num(text_size, radix, 0);
784	tbl_print_num(data_size, radix, 1);
785	tbl_print_num(bss_size, radix, 2);
786	if (radix == RADIX_OCTAL)
787		tbl_print_num(total_size, RADIX_OCTAL, 3);
788	else
789		tbl_print_num(total_size, RADIX_DECIMAL, 3);
790	tbl_print_num(total_size, RADIX_HEX, 4);
791	if (ar_name != NULL && name != NULL)
792		(void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg,
793		    name);
794	else if (ar_name != NULL && name == NULL)
795		(void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg);
796	else
797		(void) snprintf(buf, BUF_SIZE, "%s", name);
798	tbl_print(buf, 5);
799}
800
801
802static void
803tbl_new(int col)
804{
805
806	assert(tb == NULL);
807	assert(col > 0);
808	if ((tb = calloc(1, sizeof(*tb))) == NULL)
809		err(EXIT_FAILURE, "calloc");
810	if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL)
811		err(EXIT_FAILURE, "calloc");
812	if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL)
813		err(EXIT_FAILURE, "calloc");
814	tb->col = col;
815	tb->row = 0;
816}
817
818static void
819tbl_print(const char *s, int col)
820{
821	int len;
822
823	assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col);
824	assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL);
825	if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL)
826		err(EXIT_FAILURE, "strdup");
827	len = strlen(s);
828	if (len > tb->width[col])
829		tb->width[col] = len;
830}
831
832static void
833tbl_print_num(uint64_t num, enum radix_style rad, int col)
834{
835	char buf[BUF_SIZE];
836
837	(void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" :
838	    ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num);
839	tbl_print(buf, col);
840}
841
842static void
843tbl_append(void)
844{
845	int i;
846
847	assert(tb != NULL && tb->col > 0);
848	tb->row++;
849	for (i = 0; i < tb->col; i++) {
850		tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row);
851		if (tb->tbl[i] == NULL)
852			err(EXIT_FAILURE, "realloc");
853		tb->tbl[i][tb->row - 1] = NULL;
854	}
855}
856
857static void
858tbl_flush(void)
859{
860	const char *str;
861	int i, j;
862
863	if (tb == NULL)
864		return;
865
866	assert(tb->col > 0);
867	for (i = 0; i < tb->row; i++) {
868		if (style == STYLE_BERKELEY)
869			printf("  ");
870		for (j = 0; j < tb->col; j++) {
871			str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : "");
872			if (style == STYLE_SYSV && j == 0)
873				printf("%-*s", tb->width[j], str);
874			else if (style == STYLE_BERKELEY && j == tb->col - 1)
875				printf("%s", str);
876			else
877				printf("%*s", tb->width[j], str);
878			if (j == tb->col -1)
879				putchar('\n');
880			else
881				printf("   ");
882		}
883	}
884
885	for (i = 0; i < tb->col; i++) {
886		for (j = 0; j < tb->row; j++) {
887			if (tb->tbl[i][j])
888				free(tb->tbl[i][j]);
889		}
890		free(tb->tbl[i]);
891	}
892	free(tb->tbl);
893	free(tb->width);
894	free(tb);
895	tb = NULL;
896}
897
898#define	USAGE_MESSAGE	"\
899Usage: %s [options] file ...\n\
900  Display sizes of ELF sections.\n\n\
901  Options:\n\
902  --format=format    Display output in specified format.  Supported\n\
903                     values are `berkeley' and `sysv'.\n\
904  --help             Display this help message and exit.\n\
905  --radix=radix      Display numeric values in the specified radix.\n\
906                     Supported values are: 8, 10 and 16.\n\
907  --totals           Show cumulative totals of section sizes.\n\
908  --version          Display a version identifier and exit.\n\
909  -A                 Equivalent to `--format=sysv'.\n\
910  -B                 Equivalent to `--format=berkeley'.\n\
911  -V                 Equivalent to `--version'.\n\
912  -d                 Equivalent to `--radix=10'.\n\
913  -h                 Same as option --help.\n\
914  -o                 Equivalent to `--radix=8'.\n\
915  -t                 Equivalent to option --totals.\n\
916  -x                 Equivalent to `--radix=16'.\n"
917
918static void
919usage(void)
920{
921	(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
922	exit(EXIT_FAILURE);
923}
924
925static void
926show_version(void)
927{
928	(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
929	exit(EXIT_SUCCESS);
930}
931