4137c2b72e
The callers only check whether the returned pointer is non-NULL, so this was harmless in practice, but change the return value to guard against the issue. CID: 1411597 MFC after: 1 week Sponsored by: The FreeBSD Foundation
954 lines
24 KiB
C
954 lines
24 KiB
C
/*-
|
|
* Copyright (c) 2007 S.Sam Arun Raj
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <capsicum_helpers.h>
|
|
#include <err.h>
|
|
#include <fcntl.h>
|
|
#include <gelf.h>
|
|
#include <getopt.h>
|
|
#include <libelftc.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include <libcasper.h>
|
|
#include <casper/cap_fileargs.h>
|
|
|
|
#include "_elftc.h"
|
|
|
|
ELFTC_VCSID("$Id: size.c 3458 2016-05-09 15:01:25Z emaste $");
|
|
|
|
#define BUF_SIZE 1024
|
|
#define ELF_ALIGN(val,x) (((val)+(x)-1) & ~((x)-1))
|
|
#define SIZE_VERSION_STRING "size 1.0"
|
|
|
|
enum return_code {
|
|
RETURN_OK,
|
|
RETURN_DATAERR,
|
|
RETURN_USAGE
|
|
};
|
|
|
|
enum output_style {
|
|
STYLE_BERKELEY,
|
|
STYLE_SYSV
|
|
};
|
|
|
|
enum radix_style {
|
|
RADIX_OCTAL,
|
|
RADIX_DECIMAL,
|
|
RADIX_HEX
|
|
};
|
|
|
|
static uint64_t bss_size, data_size, text_size, total_size;
|
|
static uint64_t bss_size_total, data_size_total, text_size_total;
|
|
static int show_totals;
|
|
static int size_option;
|
|
static enum radix_style radix = RADIX_DECIMAL;
|
|
static enum output_style style = STYLE_BERKELEY;
|
|
|
|
static struct {
|
|
int row;
|
|
int col;
|
|
int *width;
|
|
char ***tbl;
|
|
} *tb;
|
|
|
|
enum {
|
|
OPT_FORMAT,
|
|
OPT_RADIX
|
|
};
|
|
|
|
static struct option size_longopts[] = {
|
|
{ "format", required_argument, &size_option, OPT_FORMAT },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "radix", required_argument, &size_option, OPT_RADIX },
|
|
{ "totals", no_argument, NULL, 't' },
|
|
{ "version", no_argument, NULL, 'V' },
|
|
{ NULL, 0, NULL, 0 }
|
|
};
|
|
|
|
static void berkeley_calc(GElf_Shdr *);
|
|
static void berkeley_footer(const char *, const char *, const char *);
|
|
static void berkeley_header(void);
|
|
static void berkeley_totals(void);
|
|
static int handle_core(char const *, Elf *elf, GElf_Ehdr *);
|
|
static void handle_core_note(Elf *, GElf_Ehdr *, GElf_Phdr *, char **);
|
|
static int handle_elf(int, char const *);
|
|
static void handle_phdr(Elf *, GElf_Ehdr *, GElf_Phdr *, uint32_t,
|
|
const char *);
|
|
static void show_version(void);
|
|
static void sysv_header(const char *, Elf_Arhdr *);
|
|
static void sysv_footer(void);
|
|
static void sysv_calc(Elf *, GElf_Ehdr *, GElf_Shdr *);
|
|
static void usage(void);
|
|
static void tbl_new(int);
|
|
static void tbl_print(const char *, int);
|
|
static void tbl_print_num(uint64_t, enum radix_style, int);
|
|
static void tbl_append(void);
|
|
static void tbl_flush(void);
|
|
|
|
/*
|
|
* size utility using elf(3) and gelf(3) API to list section sizes and
|
|
* total in elf files. Supports only elf files (core dumps in elf
|
|
* included) that can be opened by libelf, other formats are not supported.
|
|
*/
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
cap_rights_t rights;
|
|
fileargs_t *fa;
|
|
int ch, fd, r, rc;
|
|
const char *fn;
|
|
char *defaultfn;
|
|
|
|
rc = RETURN_OK;
|
|
|
|
if (elf_version(EV_CURRENT) == EV_NONE)
|
|
errx(EXIT_FAILURE, "ELF library initialization failed: %s",
|
|
elf_errmsg(-1));
|
|
|
|
while ((ch = getopt_long(argc, argv, "ABVdhotx", size_longopts,
|
|
NULL)) != -1)
|
|
switch((char)ch) {
|
|
case 'A':
|
|
style = STYLE_SYSV;
|
|
break;
|
|
case 'B':
|
|
style = STYLE_BERKELEY;
|
|
break;
|
|
case 'V':
|
|
show_version();
|
|
break;
|
|
case 'd':
|
|
radix = RADIX_DECIMAL;
|
|
break;
|
|
case 'o':
|
|
radix = RADIX_OCTAL;
|
|
break;
|
|
case 't':
|
|
show_totals = 1;
|
|
break;
|
|
case 'x':
|
|
radix = RADIX_HEX;
|
|
break;
|
|
case 0:
|
|
switch (size_option) {
|
|
case OPT_FORMAT:
|
|
if (*optarg == 's' || *optarg == 'S')
|
|
style = STYLE_SYSV;
|
|
else if (*optarg == 'b' || *optarg == 'B')
|
|
style = STYLE_BERKELEY;
|
|
else {
|
|
warnx("unrecognized format \"%s\".",
|
|
optarg);
|
|
usage();
|
|
}
|
|
break;
|
|
case OPT_RADIX:
|
|
r = strtol(optarg, NULL, 10);
|
|
if (r == 8)
|
|
radix = RADIX_OCTAL;
|
|
else if (r == 10)
|
|
radix = RADIX_DECIMAL;
|
|
else if (r == 16)
|
|
radix = RADIX_HEX;
|
|
else {
|
|
warnx("unsupported radix \"%s\".",
|
|
optarg);
|
|
usage();
|
|
}
|
|
break;
|
|
default:
|
|
err(EXIT_FAILURE, "Error in option handling.");
|
|
/*NOTREACHED*/
|
|
}
|
|
break;
|
|
case 'h':
|
|
case '?':
|
|
default:
|
|
usage();
|
|
/* NOTREACHED */
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
|
|
if (argc == 0) {
|
|
defaultfn = strdup("a.out");
|
|
if (defaultfn == NULL)
|
|
err(EXIT_FAILURE, "strdup");
|
|
argc = 1;
|
|
argv = &defaultfn;
|
|
} else {
|
|
defaultfn = NULL;
|
|
}
|
|
|
|
cap_rights_init(&rights, CAP_FSTAT, CAP_MMAP_R);
|
|
fa = fileargs_init(argc, argv, O_RDONLY, 0, &rights, FA_OPEN);
|
|
if (fa == NULL)
|
|
err(EXIT_FAILURE, "failed to initialize fileargs");
|
|
|
|
caph_cache_catpages();
|
|
if (caph_limit_stdio() < 0)
|
|
err(EXIT_FAILURE, "failed to limit stdio rights");
|
|
if (caph_enter_casper() < 0)
|
|
err(EXIT_FAILURE, "failed to enter capability mode");
|
|
|
|
for (; argc > 0; argc--, argv++) {
|
|
fn = argv[0];
|
|
fd = fileargs_open(fa, fn);
|
|
if (fd < 0) {
|
|
warn("%s: Failed to open", fn);
|
|
continue;
|
|
}
|
|
rc = handle_elf(fd, fn);
|
|
if (rc != RETURN_OK)
|
|
warnx("%s: File format not recognized", fn);
|
|
}
|
|
if (style == STYLE_BERKELEY) {
|
|
if (show_totals)
|
|
berkeley_totals();
|
|
tbl_flush();
|
|
}
|
|
fileargs_free(fa);
|
|
free(defaultfn);
|
|
return (rc);
|
|
}
|
|
|
|
static int
|
|
xlatetom(Elf *elf, GElf_Ehdr *elfhdr, void *_src, void *_dst,
|
|
Elf_Type type, size_t size)
|
|
{
|
|
Elf_Data src, dst;
|
|
|
|
src.d_buf = _src;
|
|
src.d_type = type;
|
|
src.d_version = elfhdr->e_version;
|
|
src.d_size = size;
|
|
dst.d_buf = _dst;
|
|
dst.d_version = elfhdr->e_version;
|
|
dst.d_size = size;
|
|
return (gelf_xlatetom(elf, &dst, &src, elfhdr->e_ident[EI_DATA]) !=
|
|
NULL ? 0 : 1);
|
|
}
|
|
|
|
#define NOTE_OFFSET_32(nhdr, namesz, offset) \
|
|
((char *)nhdr + sizeof(Elf32_Nhdr) + \
|
|
ELF_ALIGN((int32_t)namesz, 4) + offset)
|
|
|
|
#define NOTE_OFFSET_64(nhdr, namesz, offset) \
|
|
((char *)nhdr + sizeof(Elf32_Nhdr) + \
|
|
ELF_ALIGN((int32_t)namesz, 8) + offset)
|
|
|
|
#define PID32(nhdr, namesz, offset) \
|
|
(pid_t)*((int *)((uintptr_t)NOTE_OFFSET_32(nhdr, \
|
|
namesz, offset)));
|
|
|
|
#define PID64(nhdr, namesz, offset) \
|
|
(pid_t)*((int *)((uintptr_t)NOTE_OFFSET_64(nhdr, \
|
|
namesz, offset)));
|
|
|
|
#define NEXT_NOTE(elfhdr, descsz, namesz, offset) do { \
|
|
if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) { \
|
|
offset += ELF_ALIGN((int32_t)descsz, 4) + \
|
|
sizeof(Elf32_Nhdr) + \
|
|
ELF_ALIGN((int32_t)namesz, 4); \
|
|
} else { \
|
|
offset += ELF_ALIGN((int32_t)descsz, 8) + \
|
|
sizeof(Elf32_Nhdr) + \
|
|
ELF_ALIGN((int32_t)namesz, 8); \
|
|
} \
|
|
} while (0)
|
|
|
|
/*
|
|
* Parse individual note entries inside a PT_NOTE segment.
|
|
*/
|
|
static void
|
|
handle_core_note(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
|
|
char **cmd_line)
|
|
{
|
|
size_t max_size, segment_end;
|
|
uint64_t raw_size;
|
|
GElf_Off offset;
|
|
static pid_t pid;
|
|
uintptr_t ver;
|
|
Elf32_Nhdr *nhdr, nhdr_l;
|
|
static int reg_pseudo = 0, reg2_pseudo = 0 /*, regxfp_pseudo = 0*/;
|
|
char buf[BUF_SIZE], *data, *name;
|
|
|
|
if (elf == NULL || elfhdr == NULL || phdr == NULL)
|
|
return;
|
|
|
|
data = elf_rawfile(elf, &max_size);
|
|
offset = phdr->p_offset;
|
|
if (offset >= max_size || phdr->p_filesz > max_size - offset) {
|
|
warnx("invalid PHDR offset");
|
|
return;
|
|
}
|
|
segment_end = phdr->p_offset + phdr->p_filesz;
|
|
|
|
while (data != NULL && offset + sizeof(Elf32_Nhdr) < segment_end) {
|
|
nhdr = (Elf32_Nhdr *)(uintptr_t)((char*)data + offset);
|
|
memset(&nhdr_l, 0, sizeof(Elf32_Nhdr));
|
|
if (xlatetom(elf, elfhdr, &nhdr->n_type, &nhdr_l.n_type,
|
|
ELF_T_WORD, sizeof(Elf32_Word)) != 0 ||
|
|
xlatetom(elf, elfhdr, &nhdr->n_descsz, &nhdr_l.n_descsz,
|
|
ELF_T_WORD, sizeof(Elf32_Word)) != 0 ||
|
|
xlatetom(elf, elfhdr, &nhdr->n_namesz, &nhdr_l.n_namesz,
|
|
ELF_T_WORD, sizeof(Elf32_Word)) != 0)
|
|
break;
|
|
|
|
if (offset + sizeof(Elf32_Nhdr) +
|
|
ELF_ALIGN(nhdr_l.n_namesz, 4) +
|
|
ELF_ALIGN(nhdr_l.n_descsz, 4) >= segment_end) {
|
|
warnx("invalid note header");
|
|
return;
|
|
}
|
|
|
|
name = (char *)((char *)nhdr + sizeof(Elf32_Nhdr));
|
|
switch (nhdr_l.n_type) {
|
|
case NT_PRSTATUS: {
|
|
raw_size = 0;
|
|
if (elfhdr->e_ident[EI_OSABI] == ELFOSABI_FREEBSD &&
|
|
nhdr_l.n_namesz == 0x8 &&
|
|
!strcmp(name,"FreeBSD")) {
|
|
if (elfhdr->e_ident[EI_CLASS] == ELFCLASS32) {
|
|
raw_size = (uint64_t)*((uint32_t *)
|
|
(uintptr_t)(name +
|
|
ELF_ALIGN((int32_t)
|
|
nhdr_l.n_namesz, 4) + 8));
|
|
ver = (uintptr_t)NOTE_OFFSET_32(nhdr,
|
|
nhdr_l.n_namesz,0);
|
|
if (*((int *)ver) == 1)
|
|
pid = PID32(nhdr,
|
|
nhdr_l.n_namesz, 24);
|
|
} else {
|
|
raw_size = *((uint64_t *)(uintptr_t)
|
|
(name + ELF_ALIGN((int32_t)
|
|
nhdr_l.n_namesz, 8) + 16));
|
|
ver = (uintptr_t)NOTE_OFFSET_64(nhdr,
|
|
nhdr_l.n_namesz,0);
|
|
if (*((int *)ver) == 1)
|
|
pid = PID64(nhdr,
|
|
nhdr_l.n_namesz, 40);
|
|
}
|
|
(void)xlatetom(elf, elfhdr, &raw_size,
|
|
&raw_size, ELF_T_WORD, sizeof(uint64_t));
|
|
(void)xlatetom(elf, elfhdr, &pid, &pid,
|
|
ELF_T_WORD, sizeof(pid_t));
|
|
}
|
|
|
|
if (raw_size != 0 && style == STYLE_SYSV) {
|
|
(void) snprintf(buf, BUF_SIZE, "%s/%d",
|
|
".reg", pid);
|
|
tbl_append();
|
|
tbl_print(buf, 0);
|
|
tbl_print_num(raw_size, radix, 1);
|
|
tbl_print_num(0, radix, 2);
|
|
if (!reg_pseudo) {
|
|
tbl_append();
|
|
tbl_print(".reg", 0);
|
|
tbl_print_num(raw_size, radix, 1);
|
|
tbl_print_num(0, radix, 2);
|
|
reg_pseudo = 1;
|
|
text_size_total += raw_size;
|
|
}
|
|
text_size_total += raw_size;
|
|
}
|
|
}
|
|
break;
|
|
case NT_FPREGSET: /* same as NT_PRFPREG */
|
|
if (style == STYLE_SYSV) {
|
|
(void) snprintf(buf, BUF_SIZE,
|
|
"%s/%d", ".reg2", pid);
|
|
tbl_append();
|
|
tbl_print(buf, 0);
|
|
tbl_print_num(nhdr_l.n_descsz, radix, 1);
|
|
tbl_print_num(0, radix, 2);
|
|
if (!reg2_pseudo) {
|
|
tbl_append();
|
|
tbl_print(".reg2", 0);
|
|
tbl_print_num(nhdr_l.n_descsz, radix,
|
|
1);
|
|
tbl_print_num(0, radix, 2);
|
|
reg2_pseudo = 1;
|
|
text_size_total += nhdr_l.n_descsz;
|
|
}
|
|
text_size_total += nhdr_l.n_descsz;
|
|
}
|
|
break;
|
|
#if 0
|
|
case NT_AUXV:
|
|
if (style == STYLE_SYSV) {
|
|
tbl_append();
|
|
tbl_print(".auxv", 0);
|
|
tbl_print_num(nhdr_l.n_descsz, radix, 1);
|
|
tbl_print_num(0, radix, 2);
|
|
text_size_total += nhdr_l.n_descsz;
|
|
}
|
|
break;
|
|
case NT_PRXFPREG:
|
|
if (style == STYLE_SYSV) {
|
|
(void) snprintf(buf, BUF_SIZE, "%s/%d",
|
|
".reg-xfp", pid);
|
|
tbl_append();
|
|
tbl_print(buf, 0);
|
|
tbl_print_num(nhdr_l.n_descsz, radix, 1);
|
|
tbl_print_num(0, radix, 2);
|
|
if (!regxfp_pseudo) {
|
|
tbl_append();
|
|
tbl_print(".reg-xfp", 0);
|
|
tbl_print_num(nhdr_l.n_descsz, radix,
|
|
1);
|
|
tbl_print_num(0, radix, 2);
|
|
regxfp_pseudo = 1;
|
|
text_size_total += nhdr_l.n_descsz;
|
|
}
|
|
text_size_total += nhdr_l.n_descsz;
|
|
}
|
|
break;
|
|
case NT_PSINFO:
|
|
#endif
|
|
case NT_PRPSINFO: {
|
|
/* FreeBSD 64-bit */
|
|
if (nhdr_l.n_descsz == 0x78 &&
|
|
!strcmp(name,"FreeBSD")) {
|
|
*cmd_line = strdup(NOTE_OFFSET_64(nhdr,
|
|
nhdr_l.n_namesz, 33));
|
|
/* FreeBSD 32-bit */
|
|
} else if (nhdr_l.n_descsz == 0x6c &&
|
|
!strcmp(name,"FreeBSD")) {
|
|
*cmd_line = strdup(NOTE_OFFSET_32(nhdr,
|
|
nhdr_l.n_namesz, 25));
|
|
}
|
|
/* Strip any trailing spaces */
|
|
if (*cmd_line != NULL) {
|
|
char *s;
|
|
|
|
s = *cmd_line + strlen(*cmd_line);
|
|
while (s > *cmd_line) {
|
|
if (*(s-1) != 0x20) break;
|
|
s--;
|
|
}
|
|
*s = 0;
|
|
}
|
|
break;
|
|
}
|
|
#if 0
|
|
case NT_PSTATUS:
|
|
case NT_LWPSTATUS:
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
NEXT_NOTE(elfhdr, nhdr_l.n_descsz, nhdr_l.n_namesz, offset);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Handles program headers except for PT_NOTE, when sysv output style is
|
|
* chosen, prints out the segment name and length. For berkely output
|
|
* style only PT_LOAD segments are handled, and text,
|
|
* data, bss size is calculated for them.
|
|
*/
|
|
static void
|
|
handle_phdr(Elf *elf, GElf_Ehdr *elfhdr, GElf_Phdr *phdr,
|
|
uint32_t idx, const char *name)
|
|
{
|
|
uint64_t addr, size;
|
|
int split;
|
|
char buf[BUF_SIZE];
|
|
|
|
if (elf == NULL || elfhdr == NULL || phdr == NULL)
|
|
return;
|
|
|
|
split = (phdr->p_memsz > 0) && (phdr->p_filesz > 0) &&
|
|
(phdr->p_memsz > phdr->p_filesz);
|
|
|
|
if (style == STYLE_SYSV) {
|
|
(void) snprintf(buf, BUF_SIZE,
|
|
"%s%d%s", name, idx, (split ? "a" : ""));
|
|
tbl_append();
|
|
tbl_print(buf, 0);
|
|
tbl_print_num(phdr->p_filesz, radix, 1);
|
|
tbl_print_num(phdr->p_vaddr, radix, 2);
|
|
text_size_total += phdr->p_filesz;
|
|
if (split) {
|
|
size = phdr->p_memsz - phdr->p_filesz;
|
|
addr = phdr->p_vaddr + phdr->p_filesz;
|
|
(void) snprintf(buf, BUF_SIZE, "%s%d%s", name,
|
|
idx, "b");
|
|
text_size_total += phdr->p_memsz - phdr->p_filesz;
|
|
tbl_append();
|
|
tbl_print(buf, 0);
|
|
tbl_print_num(size, radix, 1);
|
|
tbl_print_num(addr, radix, 2);
|
|
}
|
|
} else {
|
|
if (phdr->p_type != PT_LOAD)
|
|
return;
|
|
if ((phdr->p_flags & PF_W) && !(phdr->p_flags & PF_X)) {
|
|
data_size += phdr->p_filesz;
|
|
if (split)
|
|
data_size += phdr->p_memsz - phdr->p_filesz;
|
|
} else {
|
|
text_size += phdr->p_filesz;
|
|
if (split)
|
|
text_size += phdr->p_memsz - phdr->p_filesz;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Given a core dump file, this function maps program headers to segments.
|
|
*/
|
|
static int
|
|
handle_core(char const *name, Elf *elf, GElf_Ehdr *elfhdr)
|
|
{
|
|
GElf_Phdr phdr;
|
|
uint32_t i;
|
|
char *core_cmdline;
|
|
const char *seg_name;
|
|
|
|
if (name == NULL || elf == NULL || elfhdr == NULL)
|
|
return (RETURN_DATAERR);
|
|
if (elfhdr->e_shnum != 0 || elfhdr->e_type != ET_CORE)
|
|
return (RETURN_DATAERR);
|
|
|
|
seg_name = core_cmdline = NULL;
|
|
if (style == STYLE_SYSV)
|
|
sysv_header(name, NULL);
|
|
else
|
|
berkeley_header();
|
|
|
|
for (i = 0; i < elfhdr->e_phnum; i++) {
|
|
if (gelf_getphdr(elf, i, &phdr) != NULL) {
|
|
if (phdr.p_type == PT_NOTE) {
|
|
handle_phdr(elf, elfhdr, &phdr, i, "note");
|
|
handle_core_note(elf, elfhdr, &phdr,
|
|
&core_cmdline);
|
|
} else {
|
|
switch(phdr.p_type) {
|
|
case PT_NULL:
|
|
seg_name = "null";
|
|
break;
|
|
case PT_LOAD:
|
|
seg_name = "load";
|
|
break;
|
|
case PT_DYNAMIC:
|
|
seg_name = "dynamic";
|
|
break;
|
|
case PT_INTERP:
|
|
seg_name = "interp";
|
|
break;
|
|
case PT_SHLIB:
|
|
seg_name = "shlib";
|
|
break;
|
|
case PT_PHDR:
|
|
seg_name = "phdr";
|
|
break;
|
|
case PT_GNU_EH_FRAME:
|
|
seg_name = "eh_frame_hdr";
|
|
break;
|
|
case PT_GNU_STACK:
|
|
seg_name = "stack";
|
|
break;
|
|
default:
|
|
seg_name = "segment";
|
|
}
|
|
handle_phdr(elf, elfhdr, &phdr, i, seg_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (style == STYLE_BERKELEY) {
|
|
if (core_cmdline != NULL) {
|
|
berkeley_footer(core_cmdline, name,
|
|
"core file invoked as");
|
|
} else {
|
|
berkeley_footer(core_cmdline, name, "core file");
|
|
}
|
|
} else {
|
|
sysv_footer();
|
|
if (core_cmdline != NULL) {
|
|
(void) printf(" (core file invoked as %s)\n\n",
|
|
core_cmdline);
|
|
} else {
|
|
(void) printf(" (core file)\n\n");
|
|
}
|
|
}
|
|
free(core_cmdline);
|
|
return (RETURN_OK);
|
|
}
|
|
|
|
/*
|
|
* Given an elf object,ar(1) filename, and based on the output style
|
|
* and radix format the various sections and their length will be printed
|
|
* or the size of the text, data, bss sections will be printed out.
|
|
*/
|
|
static int
|
|
handle_elf(int fd, const char *name)
|
|
{
|
|
GElf_Ehdr elfhdr;
|
|
GElf_Shdr shdr;
|
|
Elf *elf, *elf1;
|
|
Elf_Arhdr *arhdr;
|
|
Elf_Scn *scn;
|
|
Elf_Cmd elf_cmd;
|
|
int exit_code;
|
|
|
|
elf_cmd = ELF_C_READ;
|
|
elf1 = elf_begin(fd, elf_cmd, NULL);
|
|
while ((elf = elf_begin(fd, elf_cmd, elf1)) != NULL) {
|
|
arhdr = elf_getarhdr(elf);
|
|
if (elf_kind(elf) == ELF_K_NONE && arhdr == NULL) {
|
|
(void) elf_end(elf);
|
|
(void) elf_end(elf1);
|
|
(void) close(fd);
|
|
return (RETURN_DATAERR);
|
|
}
|
|
if (elf_kind(elf) != ELF_K_ELF ||
|
|
(gelf_getehdr(elf, &elfhdr) == NULL)) {
|
|
elf_cmd = elf_next(elf);
|
|
(void) elf_end(elf);
|
|
warnx("%s: File format not recognized",
|
|
arhdr != NULL ? arhdr->ar_name : name);
|
|
continue;
|
|
}
|
|
/* Core dumps are handled separately */
|
|
if (elfhdr.e_shnum == 0 && elfhdr.e_type == ET_CORE) {
|
|
exit_code = handle_core(name, elf, &elfhdr);
|
|
(void) elf_end(elf);
|
|
(void) elf_end(elf1);
|
|
(void) close(fd);
|
|
return (exit_code);
|
|
} else {
|
|
scn = NULL;
|
|
if (style == STYLE_BERKELEY) {
|
|
berkeley_header();
|
|
while ((scn = elf_nextscn(elf, scn)) != NULL) {
|
|
if (gelf_getshdr(scn, &shdr) != NULL)
|
|
berkeley_calc(&shdr);
|
|
}
|
|
} else {
|
|
sysv_header(name, arhdr);
|
|
scn = NULL;
|
|
while ((scn = elf_nextscn(elf, scn)) != NULL) {
|
|
if (gelf_getshdr(scn, &shdr) != NULL)
|
|
sysv_calc(elf, &elfhdr, &shdr);
|
|
}
|
|
}
|
|
if (style == STYLE_BERKELEY) {
|
|
if (arhdr != NULL) {
|
|
berkeley_footer(name, arhdr->ar_name,
|
|
"ex");
|
|
} else {
|
|
berkeley_footer(name, NULL, "ex");
|
|
}
|
|
} else {
|
|
sysv_footer();
|
|
}
|
|
}
|
|
elf_cmd = elf_next(elf);
|
|
(void) elf_end(elf);
|
|
}
|
|
(void) elf_end(elf1);
|
|
(void) close(fd);
|
|
return (RETURN_OK);
|
|
}
|
|
|
|
/*
|
|
* Sysv formatting helper functions.
|
|
*/
|
|
static void
|
|
sysv_header(const char *name, Elf_Arhdr *arhdr)
|
|
{
|
|
|
|
text_size_total = 0;
|
|
if (arhdr != NULL)
|
|
(void) printf("%s (ex %s):\n", arhdr->ar_name, name);
|
|
else
|
|
(void) printf("%s :\n", name);
|
|
tbl_new(3);
|
|
tbl_append();
|
|
tbl_print("section", 0);
|
|
tbl_print("size", 1);
|
|
tbl_print("addr", 2);
|
|
}
|
|
|
|
static void
|
|
sysv_calc(Elf *elf, GElf_Ehdr *elfhdr, GElf_Shdr *shdr)
|
|
{
|
|
char *section_name;
|
|
|
|
section_name = elf_strptr(elf, elfhdr->e_shstrndx,
|
|
(size_t) shdr->sh_name);
|
|
if ((shdr->sh_type == SHT_SYMTAB ||
|
|
shdr->sh_type == SHT_STRTAB || shdr->sh_type == SHT_RELA ||
|
|
shdr->sh_type == SHT_REL) && shdr->sh_addr == 0)
|
|
return;
|
|
tbl_append();
|
|
tbl_print(section_name, 0);
|
|
tbl_print_num(shdr->sh_size, radix, 1);
|
|
tbl_print_num(shdr->sh_addr, radix, 2);
|
|
text_size_total += shdr->sh_size;
|
|
}
|
|
|
|
static void
|
|
sysv_footer(void)
|
|
{
|
|
tbl_append();
|
|
tbl_print("Total", 0);
|
|
tbl_print_num(text_size_total, radix, 1);
|
|
tbl_flush();
|
|
putchar('\n');
|
|
}
|
|
|
|
/*
|
|
* berkeley style output formatting helper functions.
|
|
*/
|
|
static void
|
|
berkeley_header(void)
|
|
{
|
|
static int printed;
|
|
|
|
text_size = data_size = bss_size = 0;
|
|
if (!printed) {
|
|
tbl_new(6);
|
|
tbl_append();
|
|
tbl_print("text", 0);
|
|
tbl_print("data", 1);
|
|
tbl_print("bss", 2);
|
|
if (radix == RADIX_OCTAL)
|
|
tbl_print("oct", 3);
|
|
else
|
|
tbl_print("dec", 3);
|
|
tbl_print("hex", 4);
|
|
tbl_print("filename", 5);
|
|
printed = 1;
|
|
}
|
|
}
|
|
|
|
static void
|
|
berkeley_calc(GElf_Shdr *shdr)
|
|
{
|
|
if (shdr != NULL) {
|
|
if (!(shdr->sh_flags & SHF_ALLOC))
|
|
return;
|
|
if ((shdr->sh_flags & SHF_ALLOC) &&
|
|
((shdr->sh_flags & SHF_EXECINSTR) ||
|
|
!(shdr->sh_flags & SHF_WRITE)))
|
|
text_size += shdr->sh_size;
|
|
else if ((shdr->sh_flags & SHF_ALLOC) &&
|
|
(shdr->sh_flags & SHF_WRITE) &&
|
|
(shdr->sh_type != SHT_NOBITS))
|
|
data_size += shdr->sh_size;
|
|
else
|
|
bss_size += shdr->sh_size;
|
|
}
|
|
}
|
|
|
|
static void
|
|
berkeley_totals(void)
|
|
{
|
|
uint64_t grand_total;
|
|
|
|
grand_total = text_size_total + data_size_total + bss_size_total;
|
|
tbl_append();
|
|
tbl_print_num(text_size_total, radix, 0);
|
|
tbl_print_num(data_size_total, radix, 1);
|
|
tbl_print_num(bss_size_total, radix, 2);
|
|
if (radix == RADIX_OCTAL)
|
|
tbl_print_num(grand_total, RADIX_OCTAL, 3);
|
|
else
|
|
tbl_print_num(grand_total, RADIX_DECIMAL, 3);
|
|
tbl_print_num(grand_total, RADIX_HEX, 4);
|
|
}
|
|
|
|
static void
|
|
berkeley_footer(const char *name, const char *ar_name, const char *msg)
|
|
{
|
|
char buf[BUF_SIZE];
|
|
|
|
total_size = text_size + data_size + bss_size;
|
|
if (show_totals) {
|
|
text_size_total += text_size;
|
|
bss_size_total += bss_size;
|
|
data_size_total += data_size;
|
|
}
|
|
|
|
tbl_append();
|
|
tbl_print_num(text_size, radix, 0);
|
|
tbl_print_num(data_size, radix, 1);
|
|
tbl_print_num(bss_size, radix, 2);
|
|
if (radix == RADIX_OCTAL)
|
|
tbl_print_num(total_size, RADIX_OCTAL, 3);
|
|
else
|
|
tbl_print_num(total_size, RADIX_DECIMAL, 3);
|
|
tbl_print_num(total_size, RADIX_HEX, 4);
|
|
if (ar_name != NULL && name != NULL)
|
|
(void) snprintf(buf, BUF_SIZE, "%s (%s %s)", ar_name, msg,
|
|
name);
|
|
else if (ar_name != NULL && name == NULL)
|
|
(void) snprintf(buf, BUF_SIZE, "%s (%s)", ar_name, msg);
|
|
else
|
|
(void) snprintf(buf, BUF_SIZE, "%s", name);
|
|
tbl_print(buf, 5);
|
|
}
|
|
|
|
|
|
static void
|
|
tbl_new(int col)
|
|
{
|
|
|
|
assert(tb == NULL);
|
|
assert(col > 0);
|
|
if ((tb = calloc(1, sizeof(*tb))) == NULL)
|
|
err(EXIT_FAILURE, "calloc");
|
|
if ((tb->tbl = calloc(col, sizeof(*tb->tbl))) == NULL)
|
|
err(EXIT_FAILURE, "calloc");
|
|
if ((tb->width = calloc(col, sizeof(*tb->width))) == NULL)
|
|
err(EXIT_FAILURE, "calloc");
|
|
tb->col = col;
|
|
tb->row = 0;
|
|
}
|
|
|
|
static void
|
|
tbl_print(const char *s, int col)
|
|
{
|
|
int len;
|
|
|
|
assert(tb != NULL && tb->col > 0 && tb->row > 0 && col < tb->col);
|
|
assert(s != NULL && tb->tbl[col][tb->row - 1] == NULL);
|
|
if ((tb->tbl[col][tb->row - 1] = strdup(s)) == NULL)
|
|
err(EXIT_FAILURE, "strdup");
|
|
len = strlen(s);
|
|
if (len > tb->width[col])
|
|
tb->width[col] = len;
|
|
}
|
|
|
|
static void
|
|
tbl_print_num(uint64_t num, enum radix_style rad, int col)
|
|
{
|
|
char buf[BUF_SIZE];
|
|
|
|
(void) snprintf(buf, BUF_SIZE, (rad == RADIX_DECIMAL ? "%ju" :
|
|
((rad == RADIX_OCTAL) ? "0%jo" : "0x%jx")), (uintmax_t) num);
|
|
tbl_print(buf, col);
|
|
}
|
|
|
|
static void
|
|
tbl_append(void)
|
|
{
|
|
int i;
|
|
|
|
assert(tb != NULL && tb->col > 0);
|
|
tb->row++;
|
|
for (i = 0; i < tb->col; i++) {
|
|
tb->tbl[i] = realloc(tb->tbl[i], sizeof(*tb->tbl[i]) * tb->row);
|
|
if (tb->tbl[i] == NULL)
|
|
err(EXIT_FAILURE, "realloc");
|
|
tb->tbl[i][tb->row - 1] = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
tbl_flush(void)
|
|
{
|
|
const char *str;
|
|
int i, j;
|
|
|
|
if (tb == NULL)
|
|
return;
|
|
|
|
assert(tb->col > 0);
|
|
for (i = 0; i < tb->row; i++) {
|
|
if (style == STYLE_BERKELEY)
|
|
printf(" ");
|
|
for (j = 0; j < tb->col; j++) {
|
|
str = (tb->tbl[j][i] != NULL ? tb->tbl[j][i] : "");
|
|
if (style == STYLE_SYSV && j == 0)
|
|
printf("%-*s", tb->width[j], str);
|
|
else if (style == STYLE_BERKELEY && j == tb->col - 1)
|
|
printf("%s", str);
|
|
else
|
|
printf("%*s", tb->width[j], str);
|
|
if (j == tb->col -1)
|
|
putchar('\n');
|
|
else
|
|
printf(" ");
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < tb->col; i++) {
|
|
for (j = 0; j < tb->row; j++) {
|
|
if (tb->tbl[i][j])
|
|
free(tb->tbl[i][j]);
|
|
}
|
|
free(tb->tbl[i]);
|
|
}
|
|
free(tb->tbl);
|
|
free(tb->width);
|
|
free(tb);
|
|
tb = NULL;
|
|
}
|
|
|
|
#define USAGE_MESSAGE "\
|
|
Usage: %s [options] file ...\n\
|
|
Display sizes of ELF sections.\n\n\
|
|
Options:\n\
|
|
--format=format Display output in specified format. Supported\n\
|
|
values are `berkeley' and `sysv'.\n\
|
|
--help Display this help message and exit.\n\
|
|
--radix=radix Display numeric values in the specified radix.\n\
|
|
Supported values are: 8, 10 and 16.\n\
|
|
--totals Show cumulative totals of section sizes.\n\
|
|
--version Display a version identifier and exit.\n\
|
|
-A Equivalent to `--format=sysv'.\n\
|
|
-B Equivalent to `--format=berkeley'.\n\
|
|
-V Equivalent to `--version'.\n\
|
|
-d Equivalent to `--radix=10'.\n\
|
|
-h Same as option --help.\n\
|
|
-o Equivalent to `--radix=8'.\n\
|
|
-t Equivalent to option --totals.\n\
|
|
-x Equivalent to `--radix=16'.\n"
|
|
|
|
static void
|
|
usage(void)
|
|
{
|
|
(void) fprintf(stderr, USAGE_MESSAGE, ELFTC_GETPROGNAME());
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static void
|
|
show_version(void)
|
|
{
|
|
(void) printf("%s (%s)\n", ELFTC_GETPROGNAME(), elftc_version());
|
|
exit(EXIT_SUCCESS);
|
|
}
|