size.c revision 367466
1178172Simp/*-
2195162Simp * Copyright (c) 2007 S.Sam Arun Raj
3178172Simp * All rights reserved.
4178172Simp *
5178172Simp * Redistribution and use in source and binary forms, with or without
6178172Simp * modification, are permitted provided that the following conditions
7178172Simp * are met:
8178172Simp * 1. Redistributions of source code must retain the above copyright
9178172Simp *    notice, this list of conditions and the following disclaimer.
10178172Simp * 2. Redistributions in binary form must reproduce the above copyright
11178172Simp *    notice, this list of conditions and the following disclaimer in the
12178172Simp *    documentation and/or other materials provided with the distribution.
13178172Simp *
14178172Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15178172Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16178172Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17178172Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18178172Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19178172Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20178172Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21178172Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22178172Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23178172Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24178172Simp * SUCH DAMAGE.
25178172Simp */
26202046Simp
27178172Simp#include <assert.h>
28178172Simp#include <err.h>
29178172Simp#include <fcntl.h>
30178172Simp#include <gelf.h>
31178172Simp#include <getopt.h>
32202046Simp#include <libelftc.h>
33202046Simp#include <stdint.h>
34202046Simp#include <stdio.h>
35202046Simp#include <stdlib.h>
36178172Simp#include <string.h>
37178172Simp#include <unistd.h>
38178172Simp
39178172Simp#include "_elftc.h"
40178172Simp
41178172SimpELFTC_VCSID("$Id: size.c 3458 2016-05-09 15:01:25Z emaste $");
42178172Simp
43178172Simp#define	BUF_SIZE			1024
44178172Simp#define	ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1))
45178172Simp#define	SIZE_VERSION_STRING		"size 1.0"
46178172Simp
47178172Simpenum return_code {
48202046Simp	RETURN_OK,
49178172Simp	RETURN_NOINPUT,
50178172Simp	RETURN_DATAERR,
51178172Simp	RETURN_USAGE
52178172Simp};
53178172Simp
54178172Simpenum output_style {
55178172Simp	STYLE_BERKELEY,
56178172Simp	STYLE_SYSV
57178172Simp};
58204689Sneel
59202046Simpenum radix_style {
60178172Simp	RADIX_OCTAL,
61202046Simp	RADIX_DECIMAL,
62202046Simp	RADIX_HEX
63202046Simp};
64202046Simp
65202046Simpstatic uint64_t bss_size, data_size, text_size, total_size;
66202046Simpstatic uint64_t bss_size_total, data_size_total, text_size_total;
67178172Simpstatic int show_totals;
68178172Simpstatic int size_option;
69178172Simpstatic enum radix_style radix = RADIX_DECIMAL;
70232356Sjhbstatic enum output_style style = STYLE_BERKELEY;
71178172Simpstatic const char *default_args[2] = { "a.out", NULL };
72178172Simp
73178172Simpstatic struct {
74178172Simp	int row;
75178172Simp	int col;
76178172Simp	int *width;
77178172Simp	char ***tbl;
78178172Simp} *tb;
79178172Simp
80178172Simpenum {
81178172Simp	OPT_FORMAT,
82178172Simp	OPT_RADIX
83240177Sjhb};
84202046Simp
85178172Simpstatic struct option size_longopts[] = {
86178172Simp	{ "format",	required_argument, &size_option, OPT_FORMAT },
87202046Simp	{ "help",	no_argument,	NULL,	'h' },
88202046Simp	{ "radix",	required_argument, &size_option, OPT_RADIX },
89202046Simp	{ "totals",	no_argument,	NULL,	't' },
90202046Simp	{ "version",	no_argument,	NULL,	'V' },
91202046Simp	{ NULL, 0, NULL, 0 }
92202046Simp};
93202046Simp
94202046Simpstatic void	berkeley_calc(GElf_Shdr *);
95202046Simpstatic void	berkeley_footer(const char *, const char *, const char *);
96202046Simpstatic void	berkeley_header(void);
97202046Simpstatic void	berkeley_totals(void);
98202046Simpstatic int	handle_core(char const *, Elf *elf, GElf_Ehdr *);
99202046Simpstatic void	handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **);
100202046Simpstatic int	handle_elf(char const *);
101202046Simpstatic void	handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t,
102202046Simp		    const char *);
103202046Simpstatic void	show_version(void);
104202046Simpstatic void	sysv_header(const char *, Elf_Arhdr *);
105202046Simpstatic void	sysv_footer(void);
106202046Simpstatic void	sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *);
107202046Simpstatic void	usage(void);
108202046Simpstatic void	tbl_new(int);
109202046Simpstatic void	tbl_print(const char *, int);
110202046Simpstatic void	tbl_print_num(uint64_t, enum radix_style, int);
111202046Simpstatic void	tbl_append(void);
112202046Simpstatic void	tbl_flush(void);
113202046Simp
114202046Simp/*
115202046Simp * size utility using elf(3) and gelf(3) API to list section sizes and
116202046Simp * total in elf files. Supports only elf files (core dumps in elf
117202046Simp * included) that can be opened by libelf, other formats are not supported.
118202046Simp */
119202046Simpint
120202046Simpmain(int argc, char **argv)
121227309Sed{
122202046Simp	int ch, r, rc;
123202046Simp	const char **files, *fn;
124202046Simp
125178172Simp	rc = RETURN_OK;
126178172Simp
127178172Simp	if (elf_version(EV_CURRENT) == EV_NONE)
128178172Simp		errx(EXIT_FAILURE, "ELF library initialization failed: %s",
129204689Sneel		    elf_errmsg(-1));
130202046Simp
131202046Simp	while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts,
132202046Simp	    NULL)) != -1)
133178172Simp		switch((char)ch) {
134202046Simp		case 'A':
135202046Simp			style = STYLE_SYSV;
136202046Simp			break;
137212284Sjchandra		case 'B':
138178172Simp			style = STYLE_BERKELEY;
139178172Simp			break;
140178172Simp		case 'V':
141178172Simp			show_version();
142178172Simp			break;
143178172Simp		case 'd':
144202046Simp			radix = RADIX_DECIMAL;
145202046Simp			break;
146212284Sjchandra		case 'o':
147202046Simp			radix = RADIX_OCTAL;
148178172Simp			break;
149178172Simp		case 't':
150202046Simp			show_totals = 1;
151202046Simp			break;
152202046Simp		case 'x':
153178172Simp			radix = RADIX_HEX;
154178172Simp			break;
155178172Simp		case 0:
156178172Simp			switch (size_option) {
157178172Simp			case OPT_FORMAT:
158178172Simp				if (*optarg == 's' || *optarg == 'S')
159178172Simp					style = STYLE_SYSV;
160178172Simp				else if (*optarg == 'b' || *optarg == 'B')
161178172Simp					style = STYLE_BERKELEY;
162178172Simp				else {
163202046Simp					warnx("unrecognized format \"%s\".",
164202046Simp					      optarg);
165202046Simp					usage();
166202046Simp				}
167202046Simp				break;
168202046Simp			case OPT_RADIX:
169202046Simp				r = strtol(optarg, NULL, 10);
170202046Simp				if (r == 8)
171202046Simp					radix = RADIX_OCTAL;
172202046Simp				else if (r == 10)
173202046Simp					radix = RADIX_DECIMAL;
174202046Simp				else if (r == 16)
175202046Simp					radix = RADIX_HEX;
176202046Simp				else {
177202046Simp					warnx("unsupported radix \"%s\".",
178202046Simp					      optarg);
179202046Simp					usage();
180202046Simp				}
181202046Simp				break;
182202046Simp			default:
183202046Simp				err(EXIT_FAILURE, "Error in option handling.");
184202046Simp				/*NOTREACHED*/
185202046Simp			}
186202046Simp			break;
187202046Simp		case 'h':
188202046Simp		case '?':
189202046Simp		default:
190202046Simp			usage();
191202046Simp			/* NOTREACHED */
192202046Simp		}
193202046Simp	argc -= optind;
194202046Simp	argv += optind;
195202046Simp
196202046Simp	files = (argc == 0) ? default_args : (void *) argv;
197202046Simp
198202046Simp	while ((fn = *files) != NULL) {
199202046Simp		rc = handle_elf(fn);
200202046Simp		if (rc != RETURN_OK)
201202046Simp			warnx(rc == RETURN_NOINPUT ?
202178172Simp			      "'%s': No such file" :
203178172Simp			      "%s: File format not recognized", fn);
204178172Simp		files++;
205178172Simp	}
206178172Simp	if (style == STYLE_BERKELEY) {
207178172Simp		if (show_totals)
208178172Simp			berkeley_totals();
209178172Simp		tbl_flush();
210178172Simp	}
211178172Simp        return (rc);
212178172Simp}
213178172Simp
214178172Simpstatic int
215178172Simpxlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst,
216178172Simp    Elf_Type type, size_t size)
217178172Simp{
218178172Simp	Elf_Data src, dst;
219178172Simp
220178172Simp	src.d_buf = _src;
221178172Simp	src.d_type = type;
222202046Simp	src.d_version = elfhdr->e_version;
223202046Simp	src.d_size = size;
224202046Simp	dst.d_buf = _dst;
225202046Simp	dst.d_version = elfhdr->e_version;
226202046Simp	dst.d_size = size;
227202046Simp	return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA]) !=
228202046Simp	    NULL ? 0 : 1);
229202046Simp}
230202046Simp
231202046Simp#define NOTE_OFFSET_32(nhdr, namesz, offset) 			\
232202046Simp	((char *)nhdr + sizeof(Elf32_Nhdr) +			\
233202046Simp	    ELF_ALIGN((int32_t)namesz, 4) + offset)
234202046Simp
235178172Simp#define NOTE_OFFSET_64(nhdr, namesz, offset) 			\
236178172Simp	((char *)nhdr + sizeof(Elf32_Nhdr) +			\
237178172Simp	    ELF_ALIGN((int32_t)namesz, 8) + offset)
238178172Simp
239178172Simp#define PID32(nhdr, namesz, offset) 				\
240178172Simp	(pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr,	\
241178172Simp	    namesz, offset)));
242178172Simp
243178172Simp#define PID64(nhdr, namesz, offset) 				\
244178172Simp	(pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr,	\
245178172Simp	    namesz, offset)));
246178172Simp
247178172Simp#define NEXT_NOTE(elfhdr, descsz, namesz, offset) do {		\
248178172Simp	if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { 		\
249178172Simp		offset += ELF_ALIGN((int32_t)descsz, 4) +	\
250178172Simp		    sizeof(Elf32_Nhdr) + 			\
251178172Simp			ELF_ALIGN((int32_t)namesz, 4); 		\
252178172Simp	} else {						\
253178172Simp		offset += ELF_ALIGN((int32_t)descsz, 8) + 	\
254178172Simp		    sizeof(Elf32_Nhdr) + 			\
255178172Simp		        ELF_ALIGN((int32_t)namesz, 8); 		\
256178172Simp	}							\
257178172Simp} while (0)
258178172Simp
259178172Simp/*
260178172Simp * Parse individual note entries inside a PT_NOTE segment.
261178172Simp */
262178172Simpstatic void
263178172Simphandle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
264178172Simp    char **cmd_line)
265178172Simp{
266178172Simp	size_t max_size, segment_end;
267178172Simp	uint64_t raw_size;
268178172Simp	GElf_Off offset;
269178172Simp	static pid_t pid;
270178172Simp	uintptr_t ver;
271178172Simp	Elf32_Nhdr *nhdr, nhdr_l;
272178172Simp	static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/;
273178172Simp	char buf[BUF_SIZE], *data, *name;
274178172Simp
275178172Simp 	if (elf == NULL || elfhdr == NULL || phdr == NULL)
276178172Simp		return;
277178172Simp
278178172Simp	data = elf_rawfile(elf, &max_size);
279178172Simp	offset = phdr->p_offset;
280178172Simp	if (offset >= max_size || phdr->p_filesz > max_size - offset) {
281178172Simp		warnx("invalid PHDR offset");
282178172Simp		return;
283178172Simp	}
284178172Simp	segment_end = phdr->p_offset + phdr->p_filesz;
285178172Simp
286178172Simp	while (data != NULL && offset + sizeof(Elf32_Nhdr) < segment_end) {
287178172Simp		nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset);
288178172Simp		memset(&nhdr_l, 0, sizeof(Elf32_Nhdr));
289178172Simp		if (xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type,
290178172Simp		    ELF_T_WORD, sizeof(Elf32_Word)) != 0 ||
291202046Simp		    xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz,
292178172Simp		    ELF_T_WORD, sizeof(Elf32_Word)) != 0 ||
293178172Simp		    xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz,
294178172Simp		    ELF_T_WORD, sizeof(Elf32_Word)) != 0)
295178172Simp			break;
296178172Simp
297178172Simp		if (offset + sizeof(Elf32_Nhdr) +
298178172Simp		    ELF_ALIGN(nhdr_l.n_namesz, 4) +
299178172Simp		    ELF_ALIGN(nhdr_l.n_descsz, 4) >= segment_end) {
300178172Simp			warnx("invalid note header");
301178172Simp			return;
302178172Simp		}
303178172Simp
304178172Simp		name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr));
305178172Simp		switch (nhdr_l.n_type) {
306178172Simp		case NT_PRSTATUS: {
307202046Simp			raw_size = 0;
308202046Simp			if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD &&
309202046Simp			    nhdr_l.n_namesz == 0x8 &&
310202046Simp			    !strcmp(name,"FreeBSD")) {
311202046Simp				if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) {
312178172Simp					raw_size = (uint64_t)*((uint32_t *)
313178172Simp					    (uintptr_t)(name +
314232356Sjhb						ELF_ALIGN((int32_t)
315212284Sjchandra						nhdr_l.n_namesz, 4) + 8));
316212284Sjchandra					ver = (uintptr_t)NOTE_OFFSET_32(nhdr,
317212284Sjchandra					    nhdr_l.n_namesz,0);
318212284Sjchandra					if (*((int *)ver) == 1)
319178172Simp						pid = PID32(nhdr,
320178172Simp						    nhdr_l.n_namesz, 24);
321178172Simp				} else {
322178172Simp					raw_size = *((uint64_t *)(uintptr_t)
323178172Simp					    (name + ELF_ALIGN((int32_t)
324202046Simp						nhdr_l.n_namesz, 8) + 16));
325202046Simp					ver = (uintptr_t)NOTE_OFFSET_64(nhdr,
326178172Simp					    nhdr_l.n_namesz,0);
327202046Simp					if (*((int *)ver) == 1)
328178172Simp						pid = PID64(nhdr,
329178172Simp						    nhdr_l.n_namesz, 40);
330178172Simp				}
331178172Simp				(void)xlatetom(elf, elfhdr, &raw_size,
332178172Simp				    &raw_size, ELF_T_WORD, sizeof(uint64_t));
333178172Simp				(void)xlatetom(elf, elfhdr, &pid, &pid,
334178172Simp				    ELF_T_WORD, sizeof(pid_t));
335178172Simp			}
336178172Simp
337202046Simp			if (raw_size != 0 && style == STYLE_SYSV) {
338202046Simp				(void) snprintf(buf, BUF_SIZE, "%s/%d",
339178172Simp				    ".reg", pid);
340178172Simp				tbl_append();
341212284Sjchandra				tbl_print(buf, 0);
342212284Sjchandra				tbl_print_num(raw_size, radix, 1);
343178172Simp				tbl_print_num(0, radix, 2);
344178172Simp				if (!reg_pseudo) {
345204689Sneel					tbl_append();
346204689Sneel					tbl_print(".reg", 0);
347178172Simp					tbl_print_num(raw_size, radix, 1);
348178172Simp					tbl_print_num(0, radix, 2);
349178172Simp					reg_pseudo = 1;
350178172Simp					text_size_total += raw_size;
351178172Simp				}
352178172Simp				text_size_total += raw_size;
353178172Simp			}
354178172Simp		}
355178172Simp		break;
356240177Sjhb		case NT_FPREGSET:	/* same as NT_PRFPREG */
357240177Sjhb			if (style == STYLE_SYSV) {
358212284Sjchandra				(void) snprintf(buf, BUF_SIZE,
359202046Simp				    "%s/%d", ".reg2", pid);
360202046Simp				tbl_append();
361212284Sjchandra				tbl_print(buf, 0);
362232356Sjhb				tbl_print_num(nhdr_l.n_descsz, radix, 1);
363232356Sjhb				tbl_print_num(0, radix, 2);
364178172Simp				if (!reg2_pseudo) {
365178172Simp					tbl_append();
366178172Simp					tbl_print(".reg2", 0);
367212284Sjchandra					tbl_print_num(nhdr_l.n_descsz, radix,
368232356Sjhb					    1);
369202046Simp					tbl_print_num(0, radix, 2);
370202046Simp					reg2_pseudo = 1;
371202046Simp					text_size_total += nhdr_l.n_descsz;
372212284Sjchandra				}
373212284Sjchandra				text_size_total += nhdr_l.n_descsz;
374212284Sjchandra			}
375212284Sjchandra			break;
376212284Sjchandra#if 0
377212284Sjchandra		case NT_AUXV:
378212284Sjchandra			if (style == STYLE_SYSV) {
379212284Sjchandra				tbl_append();
380178172Simp				tbl_print(".auxv", 0);
381178172Simp				tbl_print_num(nhdr_l.n_descsz, radix, 1);
382178172Simp				tbl_print_num(0, radix, 2);
383178172Simp				text_size_total += nhdr_l.n_descsz;
384202046Simp			}
385202046Simp			break;
386202046Simp		case NT_PRXFPREG:
387178172Simp			if (style == STYLE_SYSV) {
388202046Simp				(void) snprintf(buf, BUF_SIZE, "%s/%d",
389202046Simp				    ".reg-xfp", pid);
390202046Simp				tbl_append();
391202046Simp				tbl_print(buf, 0);
392202046Simp				tbl_print_num(nhdr_l.n_descsz, radix, 1);
393202046Simp				tbl_print_num(0, radix, 2);
394202046Simp				if (!regxfp_pseudo) {
395202046Simp					tbl_append();
396202046Simp					tbl_print(".reg-xfp", 0);
397202046Simp					tbl_print_num(nhdr_l.n_descsz, radix,
398202046Simp					    1);
399202046Simp					tbl_print_num(0, radix, 2);
400202046Simp					regxfp_pseudo = 1;
401202046Simp					text_size_total += nhdr_l.n_descsz;
402202046Simp				}
403202046Simp				text_size_total += nhdr_l.n_descsz;
404202046Simp			}
405202046Simp			break;
406202046Simp		case NT_PSINFO:
407202046Simp#endif
408202046Simp		case NT_PRPSINFO: {
409202046Simp			/* FreeBSD 64-bit */
410202046Simp			if (nhdr_l.n_descsz == 0x78 &&
411202046Simp				!strcmp(name,"FreeBSD")) {
412202046Simp				*cmd_line = strdup(NOTE_OFFSET_64(nhdr,
413202046Simp				    nhdr_l.n_namesz, 33));
414178172Simp			/* FreeBSD 32-bit */
415202046Simp			} else if (nhdr_l.n_descsz == 0x6c &&
416178172Simp				!strcmp(name,"FreeBSD")) {
417178172Simp				*cmd_line = strdup(NOTE_OFFSET_32(nhdr,
418178172Simp				    nhdr_l.n_namesz, 25));
419202046Simp			}
420178172Simp			/* Strip any trailing spaces */
421178172Simp			if (*cmd_line != NULL) {
422178172Simp				char *s;
423178172Simp
424178172Simp				s = *cmd_line + strlen(*cmd_line);
425178172Simp				while (s > *cmd_line) {
426178172Simp					if (*(s-1) != 0x20) break;
427178172Simp					s--;
428178172Simp				}
429178172Simp				*s = 0;
430178172Simp			}
431212284Sjchandra			break;
432212284Sjchandra		}
433178172Simp#if 0
434212284Sjchandra		case NT_PSTATUS:
435212284Sjchandra		case NT_LWPSTATUS:
436178172Simp#endif
437212284Sjchandra		default:
438212284Sjchandra			break;
439212284Sjchandra		}
440240177Sjhb		NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset);
441240177Sjhb	}
442212284Sjchandra}
443212284Sjchandra
444212284Sjchandra/*
445212284Sjchandra * Handles program headers except for PT_NOTE, when sysv output style is
446212284Sjchandra * chosen, prints out the segment name and length. For berkely output
447212284Sjchandra * style only PT_LOAD segments are handled, and text,
448212284Sjchandra * data, bss size is calculated for them.
449212284Sjchandra */
450240177Sjhbstatic void
451212284Sjchandrahandle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
452212284Sjchandra    uint32_t idx, const char *name)
453178172Simp{
454178172Simp	uint64_t addr, size;
455212284Sjchandra	int split;
456178172Simp	char buf[BUF_SIZE];
457178172Simp
458202046Simp	if (elf == NULL || elfhdr == NULL || phdr == NULL)
459178172Simp		return;
460178172Simp
461178172Simp	split = (phdr->p_memsz > 0) && 	(phdr->p_filesz > 0) &&
462178172Simp	    (phdr->p_memsz > phdr->p_filesz);
463178172Simp
464178172Simp	if (style == STYLE_SYSV) {
465178172Simp		(void) snprintf(buf, BUF_SIZE,
466178172Simp		    "%s%d%s", name, idx, (split ? "a" : ""));
467178172Simp		tbl_append();
468178172Simp		tbl_print(buf, 0);
469240177Sjhb		tbl_print_num(phdr->p_filesz, radix, 1);
470240177Sjhb		tbl_print_num(phdr->p_vaddr, radix, 2);
471240177Sjhb		text_size_total += phdr->p_filesz;
472240177Sjhb		if (split) {
473240177Sjhb			size = phdr->p_memsz - phdr->p_filesz;
474240177Sjhb			addr = phdr->p_vaddr + phdr->p_filesz;
475240177Sjhb			(void) snprintf(buf, BUF_SIZE, "%s%d%s", name,
476240177Sjhb			    idx, "b");
477240177Sjhb			text_size_total += phdr->p_memsz - phdr->p_filesz;
478240177Sjhb			tbl_append();
479240177Sjhb			tbl_print(buf, 0);
480178172Simp			tbl_print_num(size, radix, 1);
481178172Simp			tbl_print_num(addr, radix, 2);
482178172Simp		}
483178172Simp	} else {
484178172Simp		if (phdr->p_type != PT_LOAD)
485178172Simp			return;
486178172Simp		if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) {
487202046Simp			data_size += phdr->p_filesz;
488178172Simp			if (split)
489178172Simp				data_size += phdr->p_memsz - phdr->p_filesz;
490202046Simp		} else {
491202046Simp			text_size += phdr->p_filesz;
492202046Simp			if (split)
493202046Simp				text_size += phdr->p_memsz - phdr->p_filesz;
494202046Simp		}
495202046Simp	}
496202046Simp}
497202046Simp
498202046Simp/*
499202046Simp * Given a core dump file, this function maps program headers to segments.
500202046Simp */
501202046Simpstatic int
502202046Simphandle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr)
503202046Simp{
504202046Simp	GElf_Phdr phdr;
505202046Simp	uint32_t i;
506202046Simp	char *core_cmdline;
507202046Simp	const char *seg_name;
508202046Simp
509202046Simp	if (name == NULL || elf == NULL || elfhdr == NULL)
510202046Simp		return (RETURN_DATAERR);
511202046Simp	if  (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE)
512202046Simp		return (RETURN_DATAERR);
513202046Simp
514202046Simp	seg_name = core_cmdline = NULL;
515202046Simp	if (style == STYLE_SYSV)
516202046Simp		sysv_header(name, NULL);
517202046Simp	else
518202046Simp		berkeley_header();
519202046Simp
520202046Simp	for (i = 0; i < elfhdr->e_phnum; i++) {
521202046Simp		if (gelf_getphdr(elf, i, &phdr) != NULL) {
522202046Simp			if (phdr.p_type == PT_NOTE) {
523202046Simp				handle_phdr(elf, elfhdr, &phdr, i, "note");
524202046Simp				handle_core_note(elf, elfhdr, &phdr,
525202046Simp				    &core_cmdline);
526202046Simp			} else {
527202046Simp				switch(phdr.p_type) {
528202046Simp				case PT_NULL:
529202046Simp					seg_name = "null";
530202046Simp					break;
531202046Simp				case PT_LOAD:
532202046Simp					seg_name = "load";
533202046Simp					break;
534202046Simp				case PT_DYNAMIC:
535202046Simp					seg_name = "dynamic";
536202046Simp					break;
537202046Simp				case PT_INTERP:
538178172Simp					seg_name = "interp";
539178172Simp					break;
540178172Simp				case PT_SHLIB:
541178172Simp					seg_name = "shlib";
542178172Simp					break;
543178172Simp				case PT_PHDR:
544178172Simp					seg_name = "phdr";
545178172Simp					break;
546178172Simp				case PT_GNU_EH_FRAME:
547178172Simp					seg_name = "eh_frame_hdr";
548178172Simp					break;
549178172Simp				case PT_GNU_STACK:
550178172Simp					seg_name = "stack";
551202046Simp					break;
552202046Simp				default:
553202046Simp					seg_name = "segment";
554202046Simp				}
555202046Simp				handle_phdr(elf, elfhdr, &phdr, i, seg_name);
556202046Simp			}
557202046Simp		}
558202046Simp	}
559178172Simp
560242465Sadrian	if (style == STYLE_BERKELEY) {
561178172Simp		if (core_cmdline != NULL) {
562178172Simp			berkeley_footer(core_cmdline, name,
563178172Simp			    "core file invoked as");
564178172Simp		} else {
565178172Simp			berkeley_footer(core_cmdline, name, "core file");
566178172Simp		}
567178172Simp	} else {
568178172Simp		sysv_footer();
569178172Simp		if (core_cmdline != NULL) {
570178172Simp			(void) printf(" (core file invoked as %s)\n\n",
571178172Simp			    core_cmdline);
572212284Sjchandra		} else {
573178172Simp			(void) printf(" (core file)\n\n");
574178172Simp		}
575178172Simp	}
576178172Simp	free(core_cmdline);
577178172Simp	return (RETURN_OK);
578178172Simp}
579178172Simp
580178172Simp/*
581178172Simp * Given an elf object,ar(1) filename, and based on the output style
582240177Sjhb * and radix format the various sections and their length will be printed
583240177Sjhb * or the size of the text, data, bss sections will be printed out.
584240177Sjhb */
585240177Sjhbstatic int
586240177Sjhbhandle_elf(char const *name)
587240177Sjhb{
588240177Sjhb	GElf_Ehdr elfhdr;
589240177Sjhb	GElf_Shdr shdr;
590240177Sjhb	Elf *elf, *elf1;
591240177Sjhb	Elf_Arhdr *arhdr;
592178172Simp	Elf_Scn *scn;
593178172Simp	Elf_Cmd elf_cmd;
594178172Simp	int exit_code, fd;
595178172Simp
596178172Simp	if (name == NULL)
597178172Simp		return (RETURN_NOINPUT);
598178172Simp
599178172Simp	if ((fd = open(name, O_RDONLY, 0)) < 0)
600178172Simp		return (RETURN_NOINPUT);
601178172Simp
602178172Simp	elf_cmd = ELF_C_READ;
603178172Simp	elf1 = elf_begin(fd, elf_cmd, NULL);
604202046Simp	while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) {
605204689Sneel		arhdr = elf_getarhdr(elf);
606204689Sneel		if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) {
607204689Sneel			(void) elf_end(elf);
608204689Sneel			(void) elf_end(elf1);
609204689Sneel			(void) close(fd);
610204689Sneel			return (RETURN_DATAERR);
611204689Sneel		}
612204689Sneel		if (elf_kind(elf) != ELF_K_ELF ||
613204689Sneel		    (gelf_getehdr(elf, &elfhdr) == NULL)) {
614204689Sneel			elf_cmd = elf_next(elf);
615202046Simp			(void) elf_end(elf);
616204689Sneel			warnx("%s: File format not recognized",
617204689Sneel			    arhdr != NULL ? arhdr->ar_name : name);
618212284Sjchandra			continue;
619202046Simp		}
620202046Simp		/* Core dumps are handled separately */
621204689Sneel		if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) {
622178172Simp			exit_code = handle_core(name, elf, &elfhdr);
623202046Simp			(void) elf_end(elf);
624212284Sjchandra			(void) elf_end(elf1);
625212284Sjchandra			(void) close(fd);
626212284Sjchandra			return (exit_code);
627212284Sjchandra		} else {
628212284Sjchandra			scn = NULL;
629212284Sjchandra			if (style == STYLE_BERKELEY) {
630212284Sjchandra				berkeley_header();
631212284Sjchandra				while ((scn = elf_nextscn(elf, scn)) != NULL) {
632212284Sjchandra					if (gelf_getshdr(scn, &shdr) != NULL)
633212284Sjchandra						berkeley_calc(&shdr);
634212284Sjchandra				}
635178172Simp			} else {
636178172Simp				sysv_header(name, arhdr);
637178172Simp				scn = NULL;
638178172Simp				while ((scn = elf_nextscn(elf, scn)) != NULL) {
639178172Simp					if (gelf_getshdr(scn, &shdr) !=	NULL)
640212284Sjchandra						sysv_calc(elf, &elfhdr, &shdr);
641178172Simp				}
642202046Simp			}
643204689Sneel			if (style == STYLE_BERKELEY) {
644178172Simp				if (arhdr != NULL) {
645178172Simp					berkeley_footer(name, arhdr->ar_name,
646178172Simp					    "ex");
647212283Sjchandra				} else {
648212283Sjchandra					berkeley_footer(name, NULL, "ex");
649178172Simp				}
650178172Simp			} else {
651178172Simp				sysv_footer();
652178172Simp			}
653178172Simp		}
654178172Simp		elf_cmd = elf_next(elf);
655178172Simp		(void) elf_end(elf);
656202046Simp	}
657178172Simp	(void) elf_end(elf1);
658202046Simp	(void) close(fd);
659212284Sjchandra	return (RETURN_OK);
660178172Simp}
661178172Simp
662178172Simp/*
663178172Simp * Sysv formatting helper functions.
664178172Simp */
665178172Simpstatic void
666178172Simpsysv_header(const char *name, Elf_Arhdr *arhdr)
667178172Simp{
668178172Simp
669178172Simp	text_size_total = 0;
670178172Simp	if (arhdr != NULL)
671178172Simp		(void) printf("%s   (ex %s):\n", arhdr->ar_name, name);
672178172Simp	else
673178172Simp		(void) printf("%s  :\n", name);
674202046Simp	tbl_new(3);
675212283Sjchandra	tbl_append();
676212283Sjchandra	tbl_print("section", 0);
677212284Sjchandra	tbl_print("size", 1);
678178172Simp	tbl_print("addr", 2);
679212284Sjchandra}
680178172Simp
681202046Simpstatic void
682178172Simpsysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr)
683178172Simp{
684178172Simp	char *section_name;
685202046Simp
686178172Simp	section_name = elf_strptr(elf, elfhdr->e_shstrndx,
687202046Simp	    (size_t) shdr->sh_name);
688202046Simp	if ((shdr->sh_type == SHT_SYMTAB ||
689202046Simp	    shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA ||
690202046Simp	    shdr->sh_type == SHT_REL) && shdr->sh_addr == 0)
691202046Simp		return;
692202046Simp	tbl_append();
693202046Simp	tbl_print(section_name, 0);
694202046Simp	tbl_print_num(shdr->sh_size, radix, 1);
695202046Simp	tbl_print_num(shdr->sh_addr, radix, 2);
696202046Simp	text_size_total += shdr->sh_size;
697202046Simp}
698202046Simp
699202046Simpstatic void
700202046Simpsysv_footer(void)
701202046Simp{
702202046Simp	tbl_append();
703202046Simp	tbl_print("Total", 0);
704206405Snwhitehorn	tbl_print_num(text_size_total, radix, 1);
705202046Simp	tbl_flush();
706202046Simp	putchar('\n');
707202046Simp}
708206405Snwhitehorn
709206405Snwhitehorn/*
710202046Simp * berkeley style output formatting helper functions.
711206405Snwhitehorn */
712202046Simpstatic void
713202046Simpberkeley_header(void)
714206405Snwhitehorn{
715206405Snwhitehorn	static int printed;
716202046Simp
717206405Snwhitehorn	text_size = data_size = bss_size = 0;
718206405Snwhitehorn	if (!printed) {
719202046Simp		tbl_new(6);
720202046Simp		tbl_append();
721202046Simp		tbl_print("text", 0);
722202046Simp		tbl_print("data", 1);
723202046Simp		tbl_print("bss", 2);
724202046Simp		if (radix == RADIX_OCTAL)
725202046Simp			tbl_print("oct", 3);
726202046Simp		else
727202046Simp			tbl_print("dec", 3);
728202046Simp		tbl_print("hex", 4);
729202046Simp		tbl_print("filename", 5);
730202046Simp		printed = 1;
731202046Simp	}
732202046Simp}
733202046Simp
734202046Simpstatic void
735202046Simpberkeley_calc(GElf_Shdr *shdr)
736202046Simp{
737202046Simp	if (shdr != NULL) {
738202046Simp		if (!(shdr->sh_flags & SHF_ALLOC))
739202046Simp			return;
740202046Simp		if ((shdr->sh_flags & SHF_ALLOC) &&
741202046Simp		    ((shdr->sh_flags & SHF_EXECINSTR) ||
742202046Simp		    !(shdr->sh_flags & SHF_WRITE)))
743202046Simp			text_size += shdr->sh_size;
744178172Simp		else if ((shdr->sh_flags & SHF_ALLOC) &&
745178172Simp		    (shdr->sh_flags & SHF_WRITE) &&
746178172Simp		    (shdr->sh_type != SHT_NOBITS))
747178172Simp			data_size += shdr->sh_size;
748178172Simp		else
749178172Simp			bss_size += shdr->sh_size;
750178172Simp	}
751178172Simp}
752178172Simp
753178172Simpstatic void
754178172Simpberkeley_totals(void)
755178172Simp{
756178172Simp	uint64_t grand_total;
757178172Simp
758202046Simp	grand_total = text_size_total + data_size_total + bss_size_total;
759178172Simp	tbl_append();
760178172Simp	tbl_print_num(text_size_total, radix, 0);
761178172Simp	tbl_print_num(data_size_total, radix, 1);
762178172Simp	tbl_print_num(bss_size_total, radix, 2);
763178172Simp	if (radix == RADIX_OCTAL)
764178172Simp		tbl_print_num(grand_total, RADIX_OCTAL, 3);
765178172Simp	else
766202046Simp		tbl_print_num(grand_total, RADIX_DECIMAL, 3);
767202046Simp	tbl_print_num(grand_total, RADIX_HEX, 4);
768202046Simp}
769202046Simp
770202046Simpstatic void
771202046Simpberkeley_footer(const char *name, const char *ar_name, const char *msg)
772202046Simp{
773202046Simp	char buf[BUF_SIZE];
774202046Simp
775178172Simp	total_size = text_size + data_size + bss_size;
776178172Simp	if (show_totals) {
777178172Simp		text_size_total += text_size;
778202046Simp		bss_size_total += bss_size;
779202046Simp		data_size_total += data_size;
780202046Simp	}
781178172Simp
782178172Simp	tbl_append();
783178172Simp	tbl_print_num(text_size, radix, 0);
784178172Simp	tbl_print_num(data_size, radix, 1);
785178172Simp	tbl_print_num(bss_size, radix, 2);
786178172Simp	if (radix == RADIX_OCTAL)
787178172Simp		tbl_print_num(total_size, RADIX_OCTAL, 3);
788178172Simp	else
789202046Simp		tbl_print_num(total_size, RADIX_DECIMAL, 3);
790202046Simp	tbl_print_num(total_size, RADIX_HEX, 4);
791178172Simp	if (ar_name != NULL && name != NULL)
792178172Simp		(void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg,
793178172Simp		    name);
794178172Simp	else if (ar_name != NULL && name == NULL)
795202046Simp		(void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg);
796202046Simp	else
797202046Simp		(void) snprintf(buf, BUF_SIZE, "%s", name);
798202046Simp	tbl_print(buf, 5);
799202046Simp}
800202046Simp
801202046Simp
802202046Simpstatic void
803202046Simptbl_new(int col)
804202046Simp{
805202046Simp
806202046Simp	assert(tb == NULL);
807202046Simp	assert(col > 0);
808178172Simp	if ((tb = calloc(1, sizeof(*tb))) == NULL)
809178172Simp		err(EXIT_FAILURE, "calloc");
810178172Simp	if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL)
811178172Simp		err(EXIT_FAILURE, "calloc");
812178172Simp	if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL)
813178172Simp		err(EXIT_FAILURE, "calloc");
814178172Simp	tb->col = col;
815178172Simp	tb->row = 0;
816178172Simp}
817178172Simp
818178172Simpstatic void
819178172Simptbl_print(const char *s, int col)
820178172Simp{
821178172Simp	int len;
822178172Simp
823178172Simp	assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col);
824178172Simp	assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL);
825178172Simp	if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL)
826178172Simp		err(EXIT_FAILURE, "strdup");
827178172Simp	len = strlen(s);
828178172Simp	if (len > tb->width[col])
829178172Simp		tb->width[col] = len;
830178172Simp}
831178172Simp
832178172Simpstatic void
833178172Simptbl_print_num(uint64_t num, enum radix_style rad, int col)
834178172Simp{
835178172Simp	char buf[BUF_SIZE];
836178172Simp
837178172Simp	(void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" :
838178172Simp	    ((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num);
839202046Simp	tbl_print(buf, col);
840202046Simp}
841178172Simp
842178172Simpstatic void
843178172Simptbl_append(void)
844178172Simp{
845178172Simp	int i;
846178172Simp
847178172Simp	assert(tb != NULL && tb->col > 0);
848212284Sjchandra	tb->row++;
849212284Sjchandra	for (i = 0; i < tb->col; i++) {
850178172Simp		tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row);
851212284Sjchandra		if (tb->tbl[i] == NULL)
852178172Simp			err(EXIT_FAILURE, "realloc");
853178172Simp		tb->tbl[i][tb->row - 1] = NULL;
854178172Simp	}
855178172Simp}
856202046Simp
857202046Simpstatic void
858178172Simptbl_flush(void)
859202046Simp{
860178172Simp	const char *str;
861178172Simp	int i, j;
862178172Simp
863240177Sjhb	if (tb == NULL)
864178172Simp		return;
865202046Simp
866202046Simp	assert(tb->col > 0);
867178172Simp	for (i = 0; i < tb->row; i++) {
868178172Simp		if (style == STYLE_BERKELEY)
869178172Simp			printf("  ");
870240177Sjhb		for (j = 0; j < tb->col; j++) {
871178172Simp			str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : "");
872178172Simp			if (style == STYLE_SYSV && j == 0)
873178172Simp				printf("%-*s", tb->width[j], str);
874178172Simp			else if (style == STYLE_BERKELEY && j == tb->col - 1)
875202046Simp				printf("%s", str);
876178172Simp			else
877178172Simp				printf("%*s", tb->width[j], str);
878178172Simp			if (j == tb->col -1)
879178172Simp				putchar('\n');
880178172Simp			else
881178172Simp				printf("   ");
882178172Simp		}
883212284Sjchandra	}
884212284Sjchandra
885178172Simp	for (i = 0; i < tb->col; i++) {
886178172Simp		for (j = 0; j < tb->row; j++) {
887178172Simp			if (tb->tbl[i][j])
888178172Simp				free(tb->tbl[i][j]);
889178172Simp		}
890178172Simp		free(tb->tbl[i]);
891202046Simp	}
892178172Simp	free(tb->tbl);
893178172Simp	free(tb->width);
894178172Simp	free(tb);
895178172Simp	tb = NULL;
896178172Simp}
897178172Simp
898178172Simp#define	USAGE_MESSAGE	"\
899178172SimpUsage: %s [options] file ...\n\
900178172Simp  Display sizes of ELF sections.\n\n\
901240177Sjhb  Options:\n\
902188506Simp  --format=format    Display output in specified format.  Supported\n\
903178172Simp                     values are `berkeley' and `sysv'.\n\
904178172Simp  --help             Display this help message and exit.\n\
905178172Simp  --radix=radix      Display numeric values in the specified radix.\n\
906178172Simp                     Supported values are: 8, 10 and 16.\n\
907178172Simp  --totals           Show cumulative totals of section sizes.\n\
908178172Simp  --version          Display a version identifier and exit.\n\
909178172Simp  -A                 Equivalent to `--format=sysv'.\n\
910178172Simp  -B                 Equivalent to `--format=berkeley'.\n\
911178172Simp  -V                 Equivalent to `--version'.\n\
912178172Simp  -d                 Equivalent to `--radix=10'.\n\
913178172Simp  -h                 Same as option --help.\n\
914240177Sjhb  -o                 Equivalent to `--radix=8'.\n\
915178172Simp  -t                 Equivalent to option --totals.\n\
916240177Sjhb  -x                 Equivalent to `--radix=16'.\n"
917178172Simp
918178172Simpstatic void
919178172Simpusage(void)
920178172Simp{
921178172Simp	(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
922178172Simp	exit(EXIT_FAILURE);
923178172Simp}
924178172Simp
925178172Simpstatic void
926178172Simpshow_version(void)
927178172Simp{
928178172Simp	(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
929178172Simp	exit(EXIT_SUCCESS);
930178172Simp}
931178172Simp