1121474Sume/*-
262614Sitojun * Copyright (c) 2007 S.Sam Arun Raj
355163Sshin * All rights reserved.
455163Sshin *
555163Sshin * Redistribution and use in source and binary forms, with or without
655163Sshin * modification, are permitted provided that the following conditions
755163Sshin * are met:
855163Sshin * 1. Redistributions of source code must retain the above copyright
955163Sshin *    notice, this list of conditions and the following disclaimer.
1055163Sshin * 2. Redistributions in binary form must reproduce the above copyright
1155163Sshin *    notice, this list of conditions and the following disclaimer in the
1255163Sshin *    documentation and/or other materials provided with the distribution.
1355163Sshin *
1455163Sshin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1555163Sshin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1655163Sshin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1755163Sshin * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1855163Sshin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1955163Sshin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2055163Sshin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2155163Sshin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2255163Sshin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2355163Sshin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2455163Sshin * SUCH DAMAGE.
2555163Sshin */
2655163Sshin
2755163Sshin#include <assert.h>
2855163Sshin#include <err.h>
2955163Sshin#include <fcntl.h>
3055163Sshin#include <gelf.h>
3155163Sshin#include <getopt.h>
3255163Sshin#include <libelftc.h>
3355163Sshin#include <stdint.h>
3455163Sshin#include <stdio.h>
3555163Sshin#include <stdlib.h>
3655163Sshin#include <string.h>
3755163Sshin#include <unistd.h>
3855163Sshin
3955163Sshin#include "_elftc.h"
4061877Sume
41105940SumeELFTC_VCSID("$Id: size.c 3458 2016-05-09 15:01:25Z emaste $");
42105940Sume
4356627Sshin#define	BUF_SIZE			1024
4456627Sshin#define	ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1))
4556627Sshin#define	SIZE_VERSION_STRING		"size 1.0"
4656627Sshin
4756627Sshinenum return_code {
4861877Sume	RETURN_OK,
4961877Sume	RETURN_NOINPUT,
5061877Sume	RETURN_DATAERR,
5161877Sume	RETURN_USAGE
52105940Sume};
53105940Sume
5461877Sumeenum output_style {
5561877Sume	STYLE_BERKELEY,
5661877Sume	STYLE_SYSV
5761877Sume};
58105940Sume
59105940Sumeenum radix_style {
60105940Sume	RADIX_OCTAL,
61105940Sume	RADIX_DECIMAL,
62105940Sume	RADIX_HEX
6355163Sshin};
6455163Sshin
6592986Sobrienstatic uint64_t bss_size, data_size, text_size, total_size;
6692986Sobrienstatic uint64_t bss_size_total, data_size_total, text_size_total;
6792986Sobrienstatic int show_totals;
6871579Sdeischenstatic int size_option;
6955163Sshinstatic enum radix_style radix = RADIX_DECIMAL;
7055163Sshinstatic enum output_style style = STYLE_BERKELEY;
7155163Sshinstatic const char *default_args[2] = { "a.out", NULL };
7255163Sshin
7355163Sshinstatic struct {
74121747Sume	int row;
75121747Sume	int col;
76121747Sume	int *width;
77121747Sume	char ***tbl;
78121747Sume} *tb;
79121747Sume
8055163Sshinenum {
8155163Sshin	OPT_FORMAT,
82121474Sume	OPT_RADIX
83121474Sume};
84121474Sume
8555163Sshinstatic struct option size_longopts[] = {
86114681Sdeischen	{ "format",	required_argument, &size_option, OPT_FORMAT },
8755163Sshin	{ "help",	no_argument,	NULL,	'h' },
8855163Sshin	{ "radix",	required_argument, &size_option, OPT_RADIX },
8955163Sshin	{ "totals",	no_argument,	NULL,	't' },
9055163Sshin	{ "version",	no_argument,	NULL,	'V' },
9155163Sshin	{ NULL, 0, NULL, 0 }
9255163Sshin};
9355163Sshin
9461877Sumestatic void	berkeley_calc(GElf_Shdr *);
95102237Spirzykstatic void	berkeley_footer(const char *, const char *, const char *);
96102237Spirzykstatic void	berkeley_header(void);
97102237Spirzykstatic void	berkeley_totals(void);
9878012Sumestatic int	handle_core(char const *, Elf *elf, GElf_Ehdr *);
9978012Sumestatic void	handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **);
10078012Sumestatic int	handle_elf(char const *);
10155163Sshinstatic void	handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t,
10265532Snectar		    const char *);
10365532Snectarstatic void	show_version(void);
10471579Sdeischenstatic void	sysv_header(const char *, Elf_Arhdr *);
105111618Snectarstatic void	sysv_footer(void);
10665532Snectarstatic void	sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *);
10755163Sshinstatic void	usage(void);
10855163Sshinstatic void	tbl_new(int);
10955163Sshinstatic void	tbl_print(const char *, int);
11055163Sshinstatic void	tbl_print_num(uint64_t, enum radix_style, int);
111105940Sumestatic void	tbl_append(void);
112105940Sumestatic void	tbl_flush(void);
113105940Sume
114105940Sume/*
11555163Sshin * size utility using elf(3) and gelf(3) API to list section sizes and
11655163Sshin * total in elf files. Supports only elf files (core dumps in elf
117105940Sume * included) that can be opened by libelf, other formats are not supported.
118105940Sume */
11955163Sshinint
12055163Sshinmain(int argc, char **argv)
12155163Sshin{
12255163Sshin	int ch, r, rc;
12355163Sshin	const char **files, *fn;
12455163Sshin
125105940Sume	rc = RETURN_OK;
12655163Sshin
127121747Sume	if (elf_version(EV_CURRENT) == EV_NONE)
128121747Sume		errx(EXIT_FAILURE, "ELF library initialization failed: %s",
129121747Sume		    elf_errmsg(-1));
130121747Sume
131121747Sume	while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts,
132121747Sume	    NULL)) != -1)
133121747Sume		switch((char)ch) {
134121747Sume		case 'A':
13555163Sshin			style = STYLE_SYSV;
13655163Sshin			break;
13755163Sshin		case 'B':
13855163Sshin			style = STYLE_BERKELEY;
13955163Sshin			break;
14055163Sshin		case 'V':
14173665Sobrien			show_version();
14255163Sshin			break;
14355163Sshin		case 'd':
14455163Sshin			radix = RADIX_DECIMAL;
14555163Sshin			break;
14655163Sshin		case 'o':
14755163Sshin			radix = RADIX_OCTAL;
14855163Sshin			break;
14955163Sshin		case 't':
15055163Sshin			show_totals = 1;
15155163Sshin			break;
15255163Sshin		case 'x':
15355163Sshin			radix = RADIX_HEX;
15455163Sshin			break;
15555163Sshin		case 0:
15655163Sshin			switch (size_option) {
15755163Sshin			case OPT_FORMAT:
15855163Sshin				if (*optarg == 's' || *optarg == 'S')
15955163Sshin					style = STYLE_SYSV;
16055163Sshin				else if (*optarg == 'b' || *optarg == 'B')
16155163Sshin					style = STYLE_BERKELEY;
16261877Sume				else {
16355163Sshin					warnx("unrecognized format \"%s\".",
16455163Sshin					      optarg);
16555163Sshin					usage();
16655163Sshin				}
167105940Sume				break;
168105940Sume			case OPT_RADIX:
169105940Sume				r = strtol(optarg, NULL, 10);
17055163Sshin				if (r == 8)
17155163Sshin					radix = RADIX_OCTAL;
17255163Sshin				else if (r == 10)
17361877Sume					radix = RADIX_DECIMAL;
174121474Sume				else if (r == 16)
17561877Sume					radix = RADIX_HEX;
17661877Sume				else {
177121474Sume					warnx("unsupported radix \"%s\".",
178121474Sume					      optarg);
179121474Sume					usage();
18061877Sume				}
181121474Sume				break;
182121474Sume			default:
183121474Sume				err(EXIT_FAILURE, "Error in option handling.");
184121474Sume				/*NOTREACHED*/
185121474Sume			}
186121474Sume			break;
18761877Sume		case 'h':
18855163Sshin		case '?':
18955163Sshin		default:
19055163Sshin			usage();
191105940Sume			/* NOTREACHED */
19255163Sshin		}
193105940Sume	argc -= optind;
19455163Sshin	argv += optind;
19555163Sshin
196121747Sume	files = (argc == 0) ? default_args : (void *) argv;
197121747Sume
198121747Sume	while ((fn = *files) != NULL) {
199121747Sume		rc = handle_elf(fn);
200121747Sume		if (rc != RETURN_OK)
201121747Sume			warnx(rc == RETURN_NOINPUT ?
202121747Sume			      "'%s': No such file" :
203121747Sume			      "%s: File format not recognized", fn);
204121747Sume		files++;
205121747Sume	}
206121747Sume	if (style == STYLE_BERKELEY) {
207121747Sume		if (show_totals)
208121747Sume			berkeley_totals();
209121747Sume		tbl_flush();
210121747Sume	}
211121747Sume        return (rc);
212121747Sume}
21365532Snectar
21465532Snectarstatic Elf_Data *
21565532Snectarxlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst,
21665532Snectar    Elf_Type type, size_t size)
21765532Snectar{
21865532Snectar	Elf_Data src, dst;
21961877Sume
22061877Sume	src.d_buf = _src;
22161877Sume	src.d_type = type;
22262614Sitojun	src.d_version = elfhdr->e_version;
22361877Sume	src.d_size = size;
22461877Sume	dst.d_buf = _dst;
22561877Sume	dst.d_version = elfhdr->e_version;
22661877Sume	dst.d_size = size;
22761877Sume	return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA]));
228121426Sume}
229121426Sume
230121426Sume#define NOTE_OFFSET_32(nhdr, namesz, offset) 			\
231121426Sume	((char *)nhdr + sizeof(Elf32_Nhdr) +			\
232121426Sume	    ELF_ALIGN((int32_t)namesz, 4) + offset)
233121426Sume
234121426Sume#define NOTE_OFFSET_64(nhdr, namesz, offset) 			\
23592905Sobrien	((char *)nhdr + sizeof(Elf32_Nhdr) +			\
23692941Sobrien	    ELF_ALIGN((int32_t)namesz, 8) + offset)
23792941Sobrien
23892941Sobrien#define PID32(nhdr, namesz, offset) 				\
239121474Sume	(pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr,	\
24092941Sobrien	    namesz, offset)));
24192941Sobrien
24292941Sobrien#define PID64(nhdr, namesz, offset) 				\
24392941Sobrien	(pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr,	\
24492941Sobrien	    namesz, offset)));
24592941Sobrien
24692905Sobrien#define NEXT_NOTE(elfhdr, descsz, namesz, offset) do {		\
24792905Sobrien	if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { 		\
24892905Sobrien		offset += ELF_ALIGN((int32_t)descsz, 4) +	\
249121474Sume		    sizeof(Elf32_Nhdr) + 			\
250121747Sume			ELF_ALIGN((int32_t)namesz, 4); 		\
25161877Sume	} else {						\
252105943Sume		offset += ELF_ALIGN((int32_t)descsz, 8) + 	\
25361877Sume		    sizeof(Elf32_Nhdr) + 			\
254121747Sume		        ELF_ALIGN((int32_t)namesz, 8); 		\
25555163Sshin	}							\
256121426Sume} while (0)
257121426Sume
258121426Sume/*
259121747Sume * Parse individual note entries inside a PT_NOTE segment.
260121747Sume */
261121747Sumestatic void
262121747Sumehandle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
263121747Sume    char **cmd_line)
264121747Sume{
26592941Sobrien	size_t max_size, segment_end;
26692941Sobrien	uint64_t raw_size;
267121426Sume	GElf_Off offset;
268121426Sume	static pid_t pid;
269121426Sume	uintptr_t ver;
270105943Sume	Elf32_Nhdr *nhdr, nhdr_l;
27192905Sobrien	static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/;
27292905Sobrien	char buf[BUF_SIZE], *data, *name;
27392905Sobrien
27492905Sobrien 	if (elf == NULL || elfhdr == NULL || phdr == NULL)
27561877Sume		return;
27692905Sobrien
27792905Sobrien	data = elf_rawfile(elf, &max_size);
27861877Sume	offset = phdr->p_offset;
27961877Sume	if (offset >= max_size || phdr->p_filesz > max_size - offset) {
28092905Sobrien		warnx("invalid PHDR offset");
28192905Sobrien		return;
28292941Sobrien	}
28392941Sobrien	segment_end = phdr->p_offset + phdr->p_filesz;
28461877Sume
285121347Sume	while (data != NULL && offset + sizeof(Elf32_Nhdr) < segment_end) {
286121347Sume		nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset);
287121347Sume		memset(&nhdr_l, 0, sizeof(Elf32_Nhdr));
288121347Sume		if (!xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type,
289121347Sume			ELF_T_WORD, sizeof(Elf32_Word)) ||
290121347Sume		    !xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz,
291121347Sume			ELF_T_WORD, sizeof(Elf32_Word)) ||
292121347Sume		    !xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz,
293121347Sume			ELF_T_WORD, sizeof(Elf32_Word)))
294121347Sume			break;
295121347Sume
296121347Sume		if (offset + sizeof(Elf32_Nhdr) +
297121347Sume		    ELF_ALIGN(nhdr_l.n_namesz, 4) +
298121347Sume		    ELF_ALIGN(nhdr_l.n_descsz, 4) >= segment_end) {
299121347Sume			warnx("invalid note header");
300121347Sume			return;
301121347Sume		}
302121347Sume
303121347Sume		name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr));
304121347Sume		switch (nhdr_l.n_type) {
30555163Sshin		case NT_PRSTATUS: {
30655163Sshin			raw_size = 0;
307104558Sume			if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD &&
308104558Sume			    nhdr_l.n_namesz == 0x8 &&
309104558Sume			    !strcmp(name,"FreeBSD")) {
310104558Sume				if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) {
311104558Sume					raw_size = (uint64_t)*((uint32_t *)
312104558Sume					    (uintptr_t)(name +
313114681Sdeischen						ELF_ALIGN((int32_t)
314104558Sume						nhdr_l.n_namesz, 4) + 8));
315114681Sdeischen					ver = (uintptr_t)NOTE_OFFSET_32(nhdr,
316104558Sume					    nhdr_l.n_namesz,0);
317114681Sdeischen					if (*((int *)ver) == 1)
318104558Sume						pid = PID32(nhdr,
31955163Sshin						    nhdr_l.n_namesz, 24);
32055163Sshin				} else {
321105940Sume					raw_size = *((uint64_t *)(uintptr_t)
32255163Sshin					    (name + ELF_ALIGN((int32_t)
32355163Sshin						nhdr_l.n_namesz, 8) + 16));
32455163Sshin					ver = (uintptr_t)NOTE_OFFSET_64(nhdr,
32555163Sshin					    nhdr_l.n_namesz,0);
32655163Sshin					if (*((int *)ver) == 1)
32755163Sshin						pid = PID64(nhdr,
32855163Sshin						    nhdr_l.n_namesz, 40);
32961877Sume				}
33055163Sshin				xlatetom(elf, elfhdr, &raw_size, &raw_size,
331105940Sume				    ELF_T_WORD, sizeof(uint64_t));
33255163Sshin				xlatetom(elf, elfhdr, &pid, &pid, ELF_T_WORD,
33355163Sshin				    sizeof(pid_t));
33455163Sshin			}
33555163Sshin
33655163Sshin			if (raw_size != 0 && style == STYLE_SYSV) {
33761877Sume				(void) snprintf(buf, BUF_SIZE, "%s/%d",
33855163Sshin				    ".reg", pid);
339105940Sume				tbl_append();
34055163Sshin				tbl_print(buf, 0);
34155163Sshin				tbl_print_num(raw_size, radix, 1);
34255163Sshin				tbl_print_num(0, radix, 2);
34355163Sshin				if (!reg_pseudo) {
34455163Sshin					tbl_append();
34561877Sume					tbl_print(".reg", 0);
34655163Sshin					tbl_print_num(raw_size, radix, 1);
347105940Sume					tbl_print_num(0, radix, 2);
34855163Sshin					reg_pseudo = 1;
34955163Sshin					text_size_total += raw_size;
35055163Sshin				}
35155163Sshin				text_size_total += raw_size;
35261877Sume			}
35361877Sume		}
35455163Sshin		break;
355105940Sume		case NT_FPREGSET:	/* same as NT_PRFPREG */
35661877Sume			if (style == STYLE_SYSV) {
357105940Sume				(void) snprintf(buf, BUF_SIZE,
35861877Sume				    "%s/%d", ".reg2", pid);
35955163Sshin				tbl_append();
36055163Sshin				tbl_print(buf, 0);
36155163Sshin				tbl_print_num(nhdr_l.n_descsz, radix, 1);
36255163Sshin				tbl_print_num(0, radix, 2);
36355163Sshin				if (!reg2_pseudo) {
364121347Sume					tbl_append();
365121347Sume					tbl_print(".reg2", 0);
366121347Sume					tbl_print_num(nhdr_l.n_descsz, radix,
367121347Sume					    1);
368121347Sume					tbl_print_num(0, radix, 2);
369121347Sume					reg2_pseudo = 1;
370121348Sume					text_size_total += nhdr_l.n_descsz;
37155163Sshin				}
37255163Sshin				text_size_total += nhdr_l.n_descsz;
37355163Sshin			}
37455163Sshin			break;
37555163Sshin#if 0
37655163Sshin		case NT_AUXV:
37755163Sshin			if (style == STYLE_SYSV) {
37855163Sshin				tbl_append();
37955163Sshin				tbl_print(".auxv", 0);
38055163Sshin				tbl_print_num(nhdr_l.n_descsz, radix, 1);
38155163Sshin				tbl_print_num(0, radix, 2);
38255163Sshin				text_size_total += nhdr_l.n_descsz;
38355163Sshin			}
38455163Sshin			break;
38561877Sume		case NT_PRXFPREG:
38661877Sume			if (style == STYLE_SYSV) {
38755163Sshin				(void) snprintf(buf, BUF_SIZE, "%s/%d",
38855163Sshin				    ".reg-xfp", pid);
38955163Sshin				tbl_append();
39055163Sshin				tbl_print(buf, 0);
39155163Sshin				tbl_print_num(nhdr_l.n_descsz, radix, 1);
39255163Sshin				tbl_print_num(0, radix, 2);
39362836Sitojun				if (!regxfp_pseudo) {
39462836Sitojun					tbl_append();
39562836Sitojun					tbl_print(".reg-xfp", 0);
39662836Sitojun					tbl_print_num(nhdr_l.n_descsz, radix,
39762836Sitojun					    1);
398105943Sume					tbl_print_num(0, radix, 2);
39962836Sitojun					regxfp_pseudo = 1;
400105943Sume					text_size_total += nhdr_l.n_descsz;
40162836Sitojun				}
40262836Sitojun				text_size_total += nhdr_l.n_descsz;
40362836Sitojun			}
40455163Sshin			break;
40555163Sshin		case NT_PSINFO:
40655163Sshin#endif
40755163Sshin		case NT_PRPSINFO: {
40855163Sshin			/* FreeBSD 64-bit */
40955163Sshin			if (nhdr_l.n_descsz == 0x78 &&
41055163Sshin				!strcmp(name,"FreeBSD")) {
41155163Sshin				*cmd_line = strdup(NOTE_OFFSET_64(nhdr,
41255163Sshin				    nhdr_l.n_namesz, 33));
41355163Sshin			/* FreeBSD 32-bit */
41455163Sshin			} else if (nhdr_l.n_descsz == 0x6c &&
415121474Sume				!strcmp(name,"FreeBSD")) {
416121474Sume				*cmd_line = strdup(NOTE_OFFSET_32(nhdr,
41755163Sshin				    nhdr_l.n_namesz, 25));
41855163Sshin			}
419121747Sume			/* Strip any trailing spaces */
42055163Sshin			if (*cmd_line != NULL) {
42161877Sume				char *s;
42255163Sshin
42355163Sshin				s = *cmd_line + strlen(*cmd_line);
42455163Sshin				while (s > *cmd_line) {
42555163Sshin					if (*(s-1) != 0x20) break;
42655163Sshin					s--;
42755163Sshin				}
42855163Sshin				*s = 0;
42955163Sshin			}
43055163Sshin			break;
43155163Sshin		}
43255163Sshin#if 0
43355163Sshin		case NT_PSTATUS:
43455163Sshin		case NT_LWPSTATUS:
43555163Sshin#endif
43655163Sshin		default:
43755163Sshin			break;
43855163Sshin		}
43955163Sshin		NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset);
44055163Sshin	}
44155163Sshin}
44255163Sshin
44355163Sshin/*
44455163Sshin * Handles program headers except for PT_NOTE, when sysv output style is
44555163Sshin * chosen, prints out the segment name and length. For berkely output
44655163Sshin * style only PT_LOAD segments are handled, and text,
44755163Sshin * data, bss size is calculated for them.
44855163Sshin */
44955163Sshinstatic void
45055163Sshinhandle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
45155163Sshin    uint32_t idx, const char *name)
45255163Sshin{
45355163Sshin	uint64_t addr, size;
45455163Sshin	int split;
45555163Sshin	char buf[BUF_SIZE];
45655163Sshin
45755163Sshin	if (elf == NULL || elfhdr == NULL || phdr == NULL)
45855163Sshin		return;
45961877Sume
460121474Sume	split = (phdr->p_memsz > 0) && 	(phdr->p_filesz > 0) &&
46161877Sume	    (phdr->p_memsz > phdr->p_filesz);
462121474Sume
46355163Sshin	if (style == STYLE_SYSV) {
464121474Sume		(void) snprintf(buf, BUF_SIZE,
46555163Sshin		    "%s%d%s", name, idx, (split ? "a" : ""));
466121474Sume		tbl_append();
467121474Sume		tbl_print(buf, 0);
468121474Sume		tbl_print_num(phdr->p_filesz, radix, 1);
469121474Sume		tbl_print_num(phdr->p_vaddr, radix, 2);
47055163Sshin		text_size_total += phdr->p_filesz;
47155163Sshin		if (split) {
47255163Sshin			size = phdr->p_memsz - phdr->p_filesz;
47355163Sshin			addr = phdr->p_vaddr + phdr->p_filesz;
47461877Sume			(void) snprintf(buf, BUF_SIZE, "%s%d%s", name,
47561877Sume			    idx, "b");
476105940Sume			text_size_total += phdr->p_memsz - phdr->p_filesz;
47761877Sume			tbl_append();
47861877Sume			tbl_print(buf, 0);
47961877Sume			tbl_print_num(size, radix, 1);
48061877Sume			tbl_print_num(addr, radix, 2);
48161877Sume		}
48261877Sume	} else {
48361877Sume		if (phdr->p_type != PT_LOAD)
48461877Sume			return;
48561877Sume		if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) {
48661877Sume			data_size += phdr->p_filesz;
48761877Sume			if (split)
48861877Sume				data_size += phdr->p_memsz - phdr->p_filesz;
48961877Sume		} else {
49061877Sume			text_size += phdr->p_filesz;
49161877Sume			if (split)
49261877Sume				text_size += phdr->p_memsz - phdr->p_filesz;
49361877Sume		}
49455163Sshin	}
49555163Sshin}
49661877Sume
49761877Sume/*
49861877Sume * Given a core dump file, this function maps program headers to segments.
49955163Sshin */
50055163Sshinstatic int
50161877Sumehandle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr)
502121474Sume{
50355163Sshin	GElf_Phdr phdr;
50455163Sshin	uint32_t i;
50561877Sume	char *core_cmdline;
50655163Sshin	const char *seg_name;
50761877Sume
50861877Sume	if (name == NULL || elf == NULL || elfhdr == NULL)
50955163Sshin		return (RETURN_DATAERR);
51055163Sshin	if  (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE)
51155163Sshin		return (RETURN_DATAERR);
51255163Sshin
51361877Sume	seg_name = core_cmdline = NULL;
51455163Sshin	if (style == STYLE_SYSV)
51555163Sshin		sysv_header(name, NULL);
51655163Sshin	else
51761877Sume		berkeley_header();
51861877Sume
51955163Sshin	for (i = 0; i < elfhdr->e_phnum; i++) {
52055163Sshin		if (gelf_getphdr(elf, i, &phdr) != NULL) {
52161877Sume			if (phdr.p_type == PT_NOTE) {
52261877Sume				handle_phdr(elf, elfhdr, &phdr, i, "note");
523121474Sume				handle_core_note(elf, elfhdr, &phdr,
524121474Sume				    &core_cmdline);
52555163Sshin			} else {
52655163Sshin				switch(phdr.p_type) {
527121474Sume				case PT_NULL:
528121474Sume					seg_name = "null";
52955163Sshin					break;
53061877Sume				case PT_LOAD:
531121474Sume					seg_name = "load";
532121474Sume					break;
533121474Sume				case PT_DYNAMIC:
534121474Sume					seg_name = "dynamic";
535121474Sume					break;
536121474Sume				case PT_INTERP:
537121474Sume					seg_name = "interp";
53855163Sshin					break;
539121474Sume				case PT_SHLIB:
540121474Sume					seg_name = "shlib";
541121474Sume					break;
542121474Sume				case PT_PHDR:
543121474Sume					seg_name = "phdr";
54455163Sshin					break;
545121474Sume				case PT_GNU_EH_FRAME:
546121474Sume					seg_name = "eh_frame_hdr";
547121474Sume					break;
548121474Sume				case PT_GNU_STACK:
54955163Sshin					seg_name = "stack";
550121474Sume					break;
551121474Sume				default:
552121474Sume					seg_name = "segment";
553121474Sume				}
554121474Sume				handle_phdr(elf, elfhdr, &phdr, i, seg_name);
55555163Sshin			}
55655163Sshin		}
557121474Sume	}
558121474Sume
559121474Sume	if (style == STYLE_BERKELEY) {
560121474Sume		if (core_cmdline != NULL) {
561121474Sume			berkeley_footer(core_cmdline, name,
562121747Sume			    "core file invoked as");
563121747Sume		} else {
564121474Sume			berkeley_footer(core_cmdline, name, "core file");
565121747Sume		}
566121474Sume	} else {
567121425Sume		sysv_footer();
568121425Sume		if (core_cmdline != NULL) {
56955163Sshin			(void) printf(" (core file invoked as %s)\n\n",
57090053Sroam			    core_cmdline);
57155163Sshin		} else {
572121474Sume			(void) printf(" (core file)\n\n");
573121474Sume		}
574121474Sume	}
57561877Sume	free(core_cmdline);
57661877Sume	return (RETURN_OK);
577121474Sume}
578121474Sume
57961877Sume/*
58061877Sume * Given an elf object,ar(1) filename, and based on the output style
58161877Sume * and radix format the various sections and their length will be printed
58255163Sshin * or the size of the text, data, bss sections will be printed out.
583121474Sume */
584121474Sumestatic int
58561877Sumehandle_elf(char const *name)
58655163Sshin{
587121474Sume	GElf_Ehdr elfhdr;
588121474Sume	GElf_Shdr shdr;
58961877Sume	Elf *elf, *elf1;
590121474Sume	Elf_Arhdr *arhdr;
591121474Sume	Elf_Scn *scn;
592121474Sume	Elf_Cmd elf_cmd;
59361877Sume	int exit_code, fd;
594121474Sume
59555163Sshin	if (name == NULL)
59661877Sume		return (RETURN_NOINPUT);
59761877Sume
59861877Sume	if ((fd = open(name, O_RDONLY, 0)) < 0)
59961877Sume		return (RETURN_NOINPUT);
60061877Sume
601121474Sume	elf_cmd = ELF_C_READ;
602121474Sume	elf1 = elf_begin(fd, elf_cmd, NULL);
60361877Sume	while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) {
60461877Sume		arhdr = elf_getarhdr(elf);
60561877Sume		if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) {
60655163Sshin			(void) elf_end(elf);
60755163Sshin			(void) elf_end(elf1);
608121747Sume			(void) close(fd);
60961877Sume			return (RETURN_DATAERR);
61061877Sume		}
61161877Sume		if (elf_kind(elf) != ELF_K_ELF ||
612121747Sume		    (gelf_getehdr(elf, &elfhdr) == NULL)) {
613121747Sume			elf_cmd = elf_next(elf);
614121747Sume			(void) elf_end(elf);
615121747Sume			warnx("%s: File format not recognized",
616121747Sume			    arhdr != NULL ? arhdr->ar_name : name);
617121747Sume			continue;
61861877Sume		}
61961877Sume		/* Core dumps are handled separately */
620121747Sume		if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) {
621121747Sume			exit_code = handle_core(name, elf, &elfhdr);
622121747Sume			(void) elf_end(elf);
623121747Sume			(void) elf_end(elf1);
624121747Sume			(void) close(fd);
625121747Sume			return (exit_code);
626121747Sume		} else {
627121747Sume			scn = NULL;
628121747Sume			if (style == STYLE_BERKELEY) {
629121747Sume				berkeley_header();
63061877Sume				while ((scn = elf_nextscn(elf, scn)) != NULL) {
631121474Sume					if (gelf_getshdr(scn, &shdr) != NULL)
63261877Sume						berkeley_calc(&shdr);
63361877Sume				}
63455163Sshin			} else {
635121747Sume				sysv_header(name, arhdr);
636121747Sume				scn = NULL;
637121474Sume				while ((scn = elf_nextscn(elf, scn)) != NULL) {
638121474Sume					if (gelf_getshdr(scn, &shdr) !=	NULL)
639121474Sume						sysv_calc(elf, &elfhdr, &shdr);
64055163Sshin				}
64155163Sshin			}
64255163Sshin			if (style == STYLE_BERKELEY) {
643121747Sume				if (arhdr != NULL) {
644121747Sume					berkeley_footer(name, arhdr->ar_name,
645121747Sume					    "ex");
646121747Sume				} else {
647121747Sume					berkeley_footer(name, NULL, "ex");
648121747Sume				}
649121747Sume			} else {
650121747Sume				sysv_footer();
651121747Sume			}
652121747Sume		}
653121747Sume		elf_cmd = elf_next(elf);
654121747Sume		(void) elf_end(elf);
655121747Sume	}
656121747Sume	(void) elf_end(elf1);
657121747Sume	(void) close(fd);
658121747Sume	return (RETURN_OK);
659121747Sume}
660121747Sume
661121747Sume/*
662121747Sume * Sysv formatting helper functions.
663121747Sume */
664121747Sumestatic void
665121747Sumesysv_header(const char *name, Elf_Arhdr *arhdr)
666121747Sume{
667121747Sume
668121747Sume	text_size_total = 0;
669121747Sume	if (arhdr != NULL)
670121747Sume		(void) printf("%s   (ex %s):\n", arhdr->ar_name, name);
671121747Sume	else
672121747Sume		(void) printf("%s  :\n", name);
673121747Sume	tbl_new(3);
674121747Sume	tbl_append();
675121747Sume	tbl_print("section", 0);
676121747Sume	tbl_print("size", 1);
677121747Sume	tbl_print("addr", 2);
678121747Sume}
679121747Sume
680121747Sumestatic void
681121747Sumesysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr)
682121747Sume{
683121747Sume	char *section_name;
684121747Sume
685121747Sume	section_name = elf_strptr(elf, elfhdr->e_shstrndx,
686121747Sume	    (size_t) shdr->sh_name);
687121747Sume	if ((shdr->sh_type == SHT_SYMTAB ||
688121747Sume	    shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA ||
689121747Sume	    shdr->sh_type == SHT_REL) && shdr->sh_addr == 0)
690121747Sume		return;
691121747Sume	tbl_append();
692121747Sume	tbl_print(section_name, 0);
693121747Sume	tbl_print_num(shdr->sh_size, radix, 1);
694121747Sume	tbl_print_num(shdr->sh_addr, radix, 2);
695121747Sume	text_size_total += shdr->sh_size;
696121747Sume}
697121747Sume
698121747Sumestatic void
699121747Sumesysv_footer(void)
700121747Sume{
701121747Sume	tbl_append();
702121747Sume	tbl_print("Total", 0);
703121747Sume	tbl_print_num(text_size_total, radix, 1);
704121747Sume	tbl_flush();
705121747Sume	putchar('\n');
706121747Sume}
707121747Sume
708121747Sume/*
709121747Sume * berkeley style output formatting helper functions.
710121747Sume */
711121747Sumestatic void
712121747Sumeberkeley_header(void)
713121747Sume{
714121747Sume	static int printed;
715121747Sume
716121747Sume	text_size = data_size = bss_size = 0;
717121747Sume	if (!printed) {
718121747Sume		tbl_new(6);
719121747Sume		tbl_append();
720121747Sume		tbl_print("text", 0);
721121747Sume		tbl_print("data", 1);
722121747Sume		tbl_print("bss", 2);
723121747Sume		if (radix == RADIX_OCTAL)
724121747Sume			tbl_print("oct", 3);
725121747Sume		else
726121747Sume			tbl_print("dec", 3);
727121747Sume		tbl_print("hex", 4);
728121747Sume		tbl_print("filename", 5);
729121747Sume		printed = 1;
730121747Sume	}
731121747Sume}
732121747Sume
733121747Sumestatic void
734121747Sumeberkeley_calc(GElf_Shdr *shdr)
735121747Sume{
736121747Sume	if (shdr != NULL) {
737121747Sume		if (!(shdr->sh_flags & SHF_ALLOC))
738121747Sume			return;
739121747Sume		if ((shdr->sh_flags & SHF_ALLOC) &&
740121747Sume		    ((shdr->sh_flags & SHF_EXECINSTR) ||
741121747Sume		    !(shdr->sh_flags & SHF_WRITE)))
742121747Sume			text_size += shdr->sh_size;
743121747Sume		else if ((shdr->sh_flags & SHF_ALLOC) &&
744121747Sume		    (shdr->sh_flags & SHF_WRITE) &&
745121747Sume		    (shdr->sh_type != SHT_NOBITS))
746121747Sume			data_size += shdr->sh_size;
747121747Sume		else
748121747Sume			bss_size += shdr->sh_size;
749121747Sume	}
750121747Sume}
751121747Sume
752121747Sumestatic void
753121747Sumeberkeley_totals(void)
754121747Sume{
755121747Sume	uint64_t grand_total;
756121747Sume
757121747Sume	grand_total = text_size_total + data_size_total + bss_size_total;
758121747Sume	tbl_append();
759121747Sume	tbl_print_num(text_size_total, radix, 0);
760121747Sume	tbl_print_num(data_size_total, radix, 1);
761121747Sume	tbl_print_num(bss_size_total, radix, 2);
762121747Sume	if (radix == RADIX_OCTAL)
763121747Sume		tbl_print_num(grand_total, RADIX_OCTAL, 3);
764121747Sume	else
765121747Sume		tbl_print_num(grand_total, RADIX_DECIMAL, 3);
766121747Sume	tbl_print_num(grand_total, RADIX_HEX, 4);
767121747Sume}
768121747Sume
769121747Sumestatic void
770121747Sumeberkeley_footer(const char *name, const char *ar_name, const char *msg)
771121747Sume{
772121747Sume	char buf[BUF_SIZE];
773121747Sume
774121747Sume	total_size = text_size + data_size + bss_size;
775121747Sume	if (show_totals) {
776121747Sume		text_size_total += text_size;
777121747Sume		bss_size_total += bss_size;
778121747Sume		data_size_total += data_size;
779121747Sume	}
780121747Sume
781121747Sume	tbl_append();
782121747Sume	tbl_print_num(text_size, radix, 0);
783121747Sume	tbl_print_num(data_size, radix, 1);
784121747Sume	tbl_print_num(bss_size, radix, 2);
785121747Sume	if (radix == RADIX_OCTAL)
786121747Sume		tbl_print_num(total_size, RADIX_OCTAL, 3);
787121747Sume	else
788121747Sume		tbl_print_num(total_size, RADIX_DECIMAL, 3);
789121747Sume	tbl_print_num(total_size, RADIX_HEX, 4);
790121747Sume	if (ar_name != NULL && name != NULL)
791121747Sume		(void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg,
792121747Sume		    name);
793121747Sume	else if (ar_name != NULL && name == NULL)
794121747Sume		(void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg);
795121747Sume	else
796121747Sume		(void) snprintf(buf, BUF_SIZE, "%s", name);
797121747Sume	tbl_print(buf, 5);
798121747Sume}
799121747Sume
800121747Sume
801121747Sumestatic void
802121747Sumetbl_new(int col)
803121747Sume{
804121747Sume
805121747Sume	assert(tb == NULL);
806121747Sume	assert(col > 0);
807121747Sume	if ((tb = calloc(1, sizeof(*tb))) == NULL)
808121747Sume		err(EXIT_FAILURE, "calloc");
809121747Sume	if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL)
810121747Sume		err(EXIT_FAILURE, "calloc");
811121747Sume	if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL)
812121747Sume		err(EXIT_FAILURE, "calloc");
813121747Sume	tb->col = col;
814121747Sume	tb->row = 0;
815121747Sume}
816121747Sume
817121747Sumestatic void
818121747Sumetbl_print(const char *s, int col)
819121747Sume{
820121747Sume	int len;
821121747Sume
822121747Sume	assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col);
823121747Sume	assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL);
824121747Sume	if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL)
825121747Sume		err(EXIT_FAILURE, "strdup");
826121747Sume	len = strlen(s);
827121747Sume	if (len > tb->width[col])
828121747Sume		tb->width[col] = len;
829121747Sume}
830121747Sume
831121747Sumestatic void
832121747Sumetbl_print_num(uint64_t num, enum radix_style rad, int col)
833121747Sume{
834121747Sume	char buf[BUF_SIZE];
835121747Sume
836121747Sume	(void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" :
837121747Sume	    ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num);
838121747Sume	tbl_print(buf, col);
839121747Sume}
840121747Sume
841121747Sumestatic void
842121747Sumetbl_append(void)
843121747Sume{
844121747Sume	int i;
845121747Sume
846121747Sume	assert(tb != NULL && tb->col > 0);
847121747Sume	tb->row++;
848121747Sume	for (i = 0; i < tb->col; i++) {
849121747Sume		tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row);
850121747Sume		if (tb->tbl[i] == NULL)
851121747Sume			err(EXIT_FAILURE, "realloc");
852121747Sume		tb->tbl[i][tb->row - 1] = NULL;
853121747Sume	}
854121747Sume}
855121747Sume
856121747Sumestatic void
857121747Sumetbl_flush(void)
858121747Sume{
859121747Sume	const char *str;
860121747Sume	int i, j;
861121747Sume
862121747Sume	if (tb == NULL)
863121747Sume		return;
864121747Sume
865121747Sume	assert(tb->col > 0);
866121747Sume	for (i = 0; i < tb->row; i++) {
867121747Sume		if (style == STYLE_BERKELEY)
868121747Sume			printf("  ");
869121747Sume		for (j = 0; j < tb->col; j++) {
870121747Sume			str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : "");
871121747Sume			if (style == STYLE_SYSV && j == 0)
872121747Sume				printf("%-*s", tb->width[j], str);
873121747Sume			else if (style == STYLE_BERKELEY && j == tb->col - 1)
874121747Sume				printf("%s", str);
875121747Sume			else
876121747Sume				printf("%*s", tb->width[j], str);
877121747Sume			if (j == tb->col -1)
878121747Sume				putchar('\n');
879121747Sume			else
880121747Sume				printf("   ");
881121747Sume		}
882121747Sume	}
883121747Sume
884121747Sume	for (i = 0; i < tb->col; i++) {
885121747Sume		for (j = 0; j < tb->row; j++) {
886121747Sume			if (tb->tbl[i][j])
887121747Sume				free(tb->tbl[i][j]);
888121747Sume		}
889121747Sume		free(tb->tbl[i]);
890121747Sume	}
891121747Sume	free(tb->tbl);
892121747Sume	free(tb->width);
893121747Sume	free(tb);
894121747Sume	tb = NULL;
895121747Sume}
896121747Sume
897121747Sume#define	USAGE_MESSAGE	"\
898121747SumeUsage: %s [options] file ...\n\
899121747Sume  Display sizes of ELF sections.\n\n\
900121747Sume  Options:\n\
901121747Sume  --format=format    Display output in specified format.  Supported\n\
902121747Sume                     values are `berkeley' and `sysv'.\n\
903121747Sume  --help             Display this help message and exit.\n\
904121747Sume  --radix=radix      Display numeric values in the specified radix.\n\
905121747Sume                     Supported values are: 8, 10 and 16.\n\
906121747Sume  --totals           Show cumulative totals of section sizes.\n\
907121747Sume  --version          Display a version identifier and exit.\n\
908121747Sume  -A                 Equivalent to `--format=sysv'.\n\
909121747Sume  -B                 Equivalent to `--format=berkeley'.\n\
910121747Sume  -V                 Equivalent to `--version'.\n\
911121747Sume  -d                 Equivalent to `--radix=10'.\n\
912121747Sume  -h                 Same as option --help.\n\
913121747Sume  -o                 Equivalent to `--radix=8'.\n\
914121747Sume  -t                 Equivalent to option --totals.\n\
915121747Sume  -x                 Equivalent to `--radix=16'.\n"
916121747Sume
917121747Sumestatic void
918121747Sumeusage(void)
919121747Sume{
920121747Sume	(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
921121747Sume	exit(EXIT_FAILURE);
922121747Sume}
923121747Sume
924121747Sumestatic void
925121747Sumeshow_version(void)
926121747Sume{
927121747Sume	(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
928121747Sume	exit(EXIT_SUCCESS);
929121747Sume}
93055163Sshin