#include <sys/param.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <_libelf.h>
#include <link.h>
#include <stdarg.h>
#include <unistd.h>
#include <libgen.h>
#include <libintl.h>
#include <locale.h>
#include <errno.h>
#include <strings.h>
#include <debug.h>
#include <conv.h>
#include <msg.h>
#include <_elfdump.h>
#include <sys/elf_SPARC.h>
#include <sys/elf_amd64.h>
#include <sys/hexdump.h>
const Cache cache_init = {NULL, NULL, NULL, NULL, 0};
typedef enum {
MATCH_ITEM_PT,
MATCH_ITEM_SHT
} match_item_t;
typedef enum {
MATCH_OPT_NAME,
MATCH_OPT_NDX,
MATCH_OPT_RANGE,
MATCH_OPT_TYPE,
} match_opt_t;
typedef struct _match {
struct _match *next;
match_opt_t opt_type;
union {
const char *name;
struct {
int start;
int end;
} ndx;
uint32_t type;
} value;
} match_rec_t;
static struct {
match_item_t item_type;
match_rec_t *list;
} match_state;
const char *
_elfdump_msg(Msg mid)
{
return (gettext(MSG_ORIG(mid)));
}
const char *
demangle(const char *name, uint_t flags)
{
if (flags & FLG_CTL_DEMANGLE)
return (Elf_demangle_name(name));
else
return ((char *)name);
}
void
failure(const char *file, const char *func)
{
(void) fprintf(stderr, MSG_INTL(MSG_ERR_FAILURE),
file, func, elf_errmsg(elf_errno()));
}
static void
detail_usage()
{
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL1));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL2));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL3));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL4));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL5));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL6));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL7));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL8));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL9));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL10));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL11));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL12));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL13));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL14));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL15));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL16));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL17));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL18));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL19));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL20));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL21));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL22));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL23));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL24));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL25));
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_DETAIL26));
}
typedef struct {
uint_t dd_indent;
} dump_data_t;
static int
dump_hex_bytes_cb(void *arg, uint64_t addr, const char *str,
size_t len __unused)
{
char index[MAXNDXSIZE];
dump_data_t *dd = arg;
size_t index_width;
(void) snprintf(index, sizeof (index), MSG_ORIG(MSG_FMT_INDEX2),
EC_WORD(addr));
index_width = strlen(index);
index_width = S_ROUND(index_width, 8);
dbg_print(0, MSG_ORIG(MSG_HEXDUMP_ROW),
dd->dd_indent, MSG_ORIG(MSG_STR_EMPTY),
index_width, index, str);
return (0);
}
void
dump_hex_bytes(const void *data, size_t n, int indent, int bytes_per_col,
int col_per_row)
{
hexdump_t h;
dump_data_t dd = {
.dd_indent = indent
};
hexdump_init(&h);
hexdump_set_grouping(&h, bytes_per_col);
hexdump_set_width(&h, bytes_per_col * col_per_row);
(void) hexdumph(&h, data, n, HDF_DOUBLESPACE, dump_hex_bytes_cb, &dd);
hexdump_fini(&h);
}
int
process_index_opt(const char *str, match_rec_t *rec)
{
#define SKIP_BLANK for (; *str && isspace(*str); str++)
char *endptr;
rec->value.ndx.start = strtol(str, &endptr, 10);
if ((str == endptr) || (rec->value.ndx.start < 0))
return (0);
str = endptr;
SKIP_BLANK;
if (*str != ':') {
rec->opt_type = MATCH_OPT_NDX;
} else {
str++;
rec->opt_type = MATCH_OPT_RANGE;
SKIP_BLANK;
if (*str == '\0') {
rec->value.ndx.end = -1;
} else {
rec->value.ndx.end = strtol(str, &endptr, 10);
if ((str == endptr) || (rec->value.ndx.end < 0))
return (0);
str = endptr;
SKIP_BLANK;
}
}
if (*str != '\0')
return (0);
return (1);
#undef SKIP_BLANK
}
typedef enum {
ATOUI_PT,
ATOUI_SHT,
ATOUI_OSABI
} atoui_type_t;
static int
atoui(const char *str, atoui_type_t type, uint32_t *v)
{
conv_strtol_uvalue_t uvalue;
char *endptr;
if (conv_iter_strtol_init(str, &uvalue) != 0) {
switch (type) {
case ATOUI_PT:
if (conv_iter_phdr_type(CONV_OSABI_ALL, CONV_FMT_ALT_CF,
conv_iter_strtol, &uvalue) == CONV_ITER_DONE)
break;
(void) conv_iter_phdr_type(CONV_OSABI_ALL,
CONV_FMT_ALT_NF, conv_iter_strtol, &uvalue);
break;
case ATOUI_SHT:
if (conv_iter_sec_type(CONV_OSABI_ALL, CONV_MACH_ALL,
CONV_FMT_ALT_CF, conv_iter_strtol, &uvalue) ==
CONV_ITER_DONE)
break;
(void) conv_iter_sec_type(CONV_OSABI_ALL, CONV_MACH_ALL,
CONV_FMT_ALT_NF, conv_iter_strtol, &uvalue);
break;
case ATOUI_OSABI:
if (conv_iter_ehdr_osabi(CONV_FMT_ALT_CF,
conv_iter_strtol, &uvalue) == CONV_ITER_DONE)
break;
(void) conv_iter_ehdr_osabi(CONV_FMT_ALT_NF,
conv_iter_strtol, &uvalue);
break;
}
if (uvalue.csl_found) {
*v = uvalue.csl_value;
return (1);
}
}
*v = strtoull(str, &endptr, 0);
for (; *endptr; endptr++)
if (!isspace(*endptr))
return (0);
return (1);
}
static int
match_prepare(char *argv0, uint_t flags)
{
match_rec_t *list;
const char *str;
int minus_p = (flags & FLG_SHOW_PHDR) != 0;
atoui_type_t atoui_type;
if (minus_p && (flags & FLG_MASK_SHOW_SHDR)) {
(void) fprintf(stderr, MSG_INTL(MSG_ERR_AMBIG_MATCH),
basename(argv0));
return (0);
}
if (minus_p) {
match_state.item_type = MATCH_ITEM_PT;
atoui_type = ATOUI_PT;
} else {
match_state.item_type = MATCH_ITEM_SHT;
atoui_type = ATOUI_SHT;
}
for (list = match_state.list; list; list = list->next) {
if ((list->opt_type == MATCH_OPT_NAME) && minus_p)
list->opt_type = MATCH_OPT_TYPE;
if (list->opt_type != MATCH_OPT_TYPE)
continue;
str = list->value.name;
if (atoui(str, atoui_type, &list->value.type) == 0) {
const char *fmt = minus_p ?
MSG_INTL(MSG_ERR_BAD_T_PT) :
MSG_INTL(MSG_ERR_BAD_T_SHT);
(void) fprintf(stderr, fmt, basename(argv0), str);
return (0);
}
}
return (1);
}
int
match(match_flags_t match_flags, const char *name, uint_t ndx, uint_t type)
{
match_item_t item_type = (match_flags & MATCH_F_PHDR) ?
MATCH_ITEM_PT : MATCH_ITEM_SHT;
match_rec_t *list;
if (match_state.list == NULL)
return ((match_flags & MATCH_F_STRICT) == 0);
if (item_type != match_state.item_type)
return (1);
for (list = match_state.list; list; list = list->next) {
switch (list->opt_type) {
case MATCH_OPT_NAME:
if (((match_flags & MATCH_F_NAME) == 0) ||
(name == NULL))
break;
if (strcmp(list->value.name, name) == 0)
return (1);
break;
case MATCH_OPT_NDX:
if ((match_flags & MATCH_F_NDX) &&
(ndx == list->value.ndx.start))
return (1);
break;
case MATCH_OPT_RANGE:
if ((match_flags & MATCH_F_NDX) &&
(ndx >= list->value.ndx.start) &&
((list->value.ndx.end < 0) ||
(ndx <= list->value.ndx.end)))
return (1);
break;
case MATCH_OPT_TYPE:
if ((match_flags & MATCH_F_TYPE) &&
(type == list->value.type))
return (1);
break;
}
}
return (0);
}
static int
add_match_record(char *argv0, match_rec_t *data)
{
match_rec_t *rec;
match_rec_t *list;
if ((rec = malloc(sizeof (*rec))) == NULL) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_ERR_MALLOC),
basename(argv0), strerror(err));
return (0);
}
*rec = *data;
if (match_state.list == NULL) {
match_state.list = rec;
} else {
for (list = match_state.list; list->next != NULL;
list = list->next)
;
list->next = rec;
}
rec->next = NULL;
return (1);
}
static int
decide(const char *file, int fd, Elf *elf, uint_t flags,
const char *wname, int wfd, uchar_t osabi)
{
int r;
if (gelf_getclass(elf) == ELFCLASS64)
r = regular64(file, fd, elf, flags, wname, wfd, osabi);
else
r = regular32(file, fd, elf, flags, wname, wfd, osabi);
return (r);
}
static int
archive(const char *file, int fd, Elf *elf, uint_t flags,
const char *wname, int wfd, uchar_t osabi)
{
Elf_Cmd cmd = ELF_C_READ;
Elf_Arhdr *arhdr;
Elf *_elf = NULL;
size_t ptr;
Elf_Arsym *arsym = NULL;
if ((flags & FLG_SHOW_SYMBOLS) &&
match(MATCH_F_NAME, MSG_ORIG(MSG_ELF_ARSYM), 0, 0)) {
if (((arsym = elf_getarsym(elf, &ptr)) == 0) && elf_errno()) {
failure(file, MSG_ORIG(MSG_ELF_GETARSYM));
return (0);
}
}
if (arsym) {
size_t cnt;
char index[MAXNDXSIZE];
size_t offset = 0, _offset = 0;
const char *fmt_arsym1, *fmt_arsym2;
if (_elf_getarsymwordsize(elf) == 8) {
dbg_print(0, MSG_INTL(MSG_ARCHIVE_SYMTAB_64));
dbg_print(0, MSG_INTL(MSG_ARCHIVE_FIELDS_64));
fmt_arsym1 = MSG_ORIG(MSG_FMT_ARSYM1_64);
fmt_arsym2 = MSG_ORIG(MSG_FMT_ARSYM2_64);
} else {
dbg_print(0, MSG_INTL(MSG_ARCHIVE_SYMTAB_32));
dbg_print(0, MSG_INTL(MSG_ARCHIVE_FIELDS_32));
fmt_arsym1 = MSG_ORIG(MSG_FMT_ARSYM1_32);
fmt_arsym2 = MSG_ORIG(MSG_FMT_ARSYM2_32);
}
for (cnt = 0; cnt < ptr; cnt++, arsym++) {
if ((offset == 0) || ((arsym->as_off != 0) &&
(arsym->as_off != _offset))) {
if (_elf)
(void) elf_end(_elf);
if (elf_rand(elf, arsym->as_off) !=
arsym->as_off) {
failure(file, MSG_ORIG(MSG_ELF_RAND));
arhdr = NULL;
} else if ((_elf = elf_begin(fd,
ELF_C_READ, elf)) == 0) {
failure(file, MSG_ORIG(MSG_ELF_BEGIN));
arhdr = NULL;
} else if ((arhdr = elf_getarhdr(_elf)) == 0) {
failure(file,
MSG_ORIG(MSG_ELF_GETARHDR));
arhdr = NULL;
}
_offset = arsym->as_off;
if (offset == 0)
offset = _offset;
}
(void) snprintf(index, MAXNDXSIZE,
MSG_ORIG(MSG_FMT_INDEX), EC_XWORD(cnt));
if (arsym->as_off)
dbg_print(0, fmt_arsym1, index,
EC_XWORD(arsym->as_off),
arhdr ? arhdr->ar_name :
MSG_INTL(MSG_STR_UNKNOWN), (arsym->as_name ?
demangle(arsym->as_name, flags) :
MSG_INTL(MSG_STR_NULL)));
else
dbg_print(0, fmt_arsym2, index,
EC_XWORD(arsym->as_off));
}
if (_elf)
(void) elf_end(_elf);
if ((flags & FLG_SHOW_SYMBOLS) &&
match(MATCH_F_STRICT | MATCH_F_NAME,
MSG_ORIG(MSG_ELF_ARSYM), -1, -1))
return (0);
if (offset)
(void) elf_rand(elf, offset);
}
while ((_elf = elf_begin(fd, cmd, elf)) != NULL) {
char name[MAXPATHLEN];
if ((arhdr = elf_getarhdr(_elf)) == NULL) {
failure(file, MSG_ORIG(MSG_ELF_GETARHDR));
return (0);
}
if (*arhdr->ar_name != '/') {
(void) snprintf(name, MAXPATHLEN,
MSG_ORIG(MSG_FMT_ARNAME), file, arhdr->ar_name);
dbg_print(0, MSG_ORIG(MSG_FMT_NLSTR), name);
switch (elf_kind(_elf)) {
case ELF_K_AR:
if (archive(name, fd, _elf, flags,
wname, wfd, osabi) == 1)
return (1);
break;
case ELF_K_ELF:
if (decide(name, fd, _elf, flags,
wname, wfd, osabi) == 1)
return (1);
break;
default:
(void) fprintf(stderr,
MSG_INTL(MSG_ERR_BADFILE), name);
break;
}
}
cmd = elf_next(_elf);
(void) elf_end(_elf);
}
return (0);
}
int
main(int argc, char **argv, char **envp)
{
Elf *elf;
int var, fd, wfd = 0;
char *wname = NULL;
uint_t flags = 0;
match_rec_t match_data;
int ret;
uchar_t osabi = ELFOSABI_NONE;
(void) setlocale(LC_MESSAGES, MSG_ORIG(MSG_STR_EMPTY));
(void) textdomain(MSG_ORIG(MSG_SUNW_OST_SGS));
(void) setvbuf(stdout, NULL, _IOLBF, 0);
(void) setvbuf(stderr, NULL, _IOLBF, 0);
opterr = 0;
while ((var = getopt(argc, argv, MSG_ORIG(MSG_STR_OPTIONS))) != EOF) {
switch (var) {
case 'C':
flags |= FLG_CTL_DEMANGLE;
break;
case 'c':
flags |= FLG_SHOW_SHDR;
break;
case 'd':
flags |= FLG_SHOW_DYNAMIC;
break;
case 'e':
flags |= FLG_SHOW_EHDR;
break;
case 'G':
flags |= FLG_SHOW_GOT;
break;
case 'g':
flags |= FLG_SHOW_GROUP;
break;
case 'H':
flags |= FLG_SHOW_CAP;
break;
case 'h':
flags |= FLG_SHOW_HASH;
break;
case 'I':
if (!process_index_opt(optarg, &match_data))
goto usage_brief;
if (!add_match_record(argv[0], &match_data))
return (1);
flags |= FLG_CTL_MATCH;
break;
case 'i':
flags |= FLG_SHOW_INTERP;
break;
case 'k':
flags |= FLG_CALC_CHECKSUM;
break;
case 'l':
flags |= FLG_CTL_LONGNAME;
break;
case 'm':
flags |= FLG_SHOW_MOVE;
break;
case 'N':
match_data.opt_type = MATCH_OPT_NAME;
match_data.value.name = optarg;
if (!add_match_record(argv[0], &match_data))
return (1);
flags |= FLG_CTL_MATCH;
break;
case 'n':
flags |= FLG_SHOW_NOTE;
break;
case 'O':
{
uint32_t val;
if ((atoui(optarg, ATOUI_OSABI, &val) == 0) ||
(val > 255)) {
(void) fprintf(stderr,
MSG_INTL(MSG_ERR_BAD_T_OSABI),
basename(argv[0]), optarg);
return (1);
}
osabi = val;
}
flags |= FLG_CTL_OSABI;
break;
case 'P':
flags |= FLG_CTL_FAKESHDR;
break;
case 'p':
flags |= FLG_SHOW_PHDR;
break;
case 'r':
flags |= FLG_SHOW_RELOC;
break;
case 'S':
flags |= FLG_SHOW_SORT;
break;
case 's':
flags |= FLG_SHOW_SYMBOLS;
break;
case 'T':
match_data.opt_type = MATCH_OPT_TYPE;
match_data.value.name = optarg;
if (!add_match_record(argv[0], &match_data))
return (1);
flags |= FLG_CTL_MATCH;
break;
case 'u':
flags |= FLG_SHOW_UNWIND;
break;
case 'v':
flags |= FLG_SHOW_VERSIONS;
break;
case 'w':
wname = optarg;
break;
case 'y':
flags |= FLG_SHOW_SYMINFO;
break;
case '?':
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
basename(argv[0]));
detail_usage();
return (1);
default:
break;
}
}
if (((flags & FLG_SHOW_PHDR) != 0) && (wname != NULL))
goto usage_brief;
if ((match_state.list != NULL) && (match_prepare(argv[0], flags) == 0))
return (1);
if ((flags & ~FLG_MASK_CTL) == 0) {
if (!wname && (match_state.list == NULL))
flags |= FLG_MASK_SHOW;
else if (match_state.list == NULL)
goto usage_brief;
}
if ((var = argc - optind) == 0)
goto usage_brief;
if (flags & FLG_CTL_LONGNAME)
dbg_desc->d_extra |= DBG_E_LONG;
if (flags & FLG_CTL_DEMANGLE)
dbg_desc->d_extra |= DBG_E_DEMANGLE;
if (wname) {
if ((wfd = open(wname, (O_RDWR | O_CREAT | O_TRUNC),
0666)) < 0) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN),
wname, strerror(err));
return (1);
}
}
ret = 0;
for (; (optind < argc) && (ret == 0); optind++) {
const char *file = argv[optind];
if ((fd = open(argv[optind], O_RDONLY)) == -1) {
int err = errno;
(void) fprintf(stderr, MSG_INTL(MSG_ERR_OPEN),
file, strerror(err));
continue;
}
(void) elf_version(EV_CURRENT);
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
failure(file, MSG_ORIG(MSG_ELF_BEGIN));
(void) close(fd);
continue;
}
if (var > 1)
dbg_print(0, MSG_ORIG(MSG_FMT_NLSTRNL), file);
switch (elf_kind(elf)) {
case ELF_K_AR:
ret = archive(file, fd, elf, flags, wname, wfd, osabi);
break;
case ELF_K_ELF:
ret = decide(file, fd, elf, flags, wname, wfd, osabi);
break;
default:
(void) fprintf(stderr, MSG_INTL(MSG_ERR_BADFILE), file);
break;
}
(void) close(fd);
(void) elf_end(elf);
}
if (wfd)
(void) close(wfd);
return (ret);
usage_brief:
(void) fprintf(stderr, MSG_INTL(MSG_USAGE_BRIEF),
basename(argv[0]));
return (1);
}