size.c revision 275370
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
42ELFTC_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
48enum return_code {
49	RETURN_OK,
50	RETURN_NOINPUT,
51	RETURN_DATAERR,
52	RETURN_USAGE
53};
54
55enum output_style {
56	STYLE_BERKELEY,
57	STYLE_SYSV
58};
59
60enum radix_style {
61	RADIX_OCTAL,
62	RADIX_DECIMAL,
63	RADIX_HEX
64};
65
66static uint64_t bss_size, data_size, text_size, total_size;
67static uint64_t bss_size_total, data_size_total, text_size_total;
68static int show_totals;
69static int size_option;
70static enum radix_style radix = RADIX_DECIMAL;
71static enum output_style style = STYLE_BERKELEY;
72static const char *default_args[2] = { "a.out", NULL };
73
74static struct {
75	int row;
76	int col;
77	int *width;
78	char ***tbl;
79} *tb;
80
81enum {
82	OPT_FORMAT,
83	OPT_RADIX
84};
85
86static 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
95static void	berkeley_calc(GElf_Shdr *);
96static void	berkeley_footer(const char *, const char *, const char *);
97static void	berkeley_header(void);
98static void	berkeley_totals(void);
99static int	handle_core(char const *, Elf *elf, GElf_Ehdr *);
100static void	handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **);
101static int	handle_elf(char const *);
102static void	handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t,
103		    const char *);
104static void	show_version(void);
105static void	sysv_header(const char *, Elf_Arhdr *);
106static void	sysv_footer(void);
107static void	sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *);
108static void	usage(void);
109static void	tbl_new(int);
110static void	tbl_print(const char *, int);
111static void	tbl_print_num(uint64_t, enum radix_style, int);
112static void	tbl_append(void);
113static 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 */
120int
121main(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
215static Elf_Data *
216xlatetom(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 */
262static void
263handle_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 */
437static void
438handle_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 */
489static int
490handle_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 */
573static int
574handle_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 */
653static void
654sysv_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
669static void
670sysv_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
687static void
688sysv_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 */
700static void
701berkeley_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
722static void
723berkeley_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
741static void
742berkeley_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
758static void
759berkeley_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
790static void
791tbl_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
806static void
807tbl_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
820static void
821tbl_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
830static void
831tbl_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
845static void
846tbl_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	"\
887Usage: %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
906static void
907usage(void)
908{
909	(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
910	exit(EXIT_FAILURE);
911}
912
913static void
914show_version(void)
915{
916	(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
917	exit(EXIT_SUCCESS);
918}
919