xref: /onnv-gate/usr/src/cmd/dis/dis_target.c (revision 2355:68152b7be3a7)
11545Seschrock /*
21545Seschrock  * CDDL HEADER START
31545Seschrock  *
41545Seschrock  * The contents of this file are subject to the terms of the
51545Seschrock  * Common Development and Distribution License (the "License").
61545Seschrock  * You may not use this file except in compliance with the License.
71545Seschrock  *
81545Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91545Seschrock  * or http://www.opensolaris.org/os/licensing.
101545Seschrock  * See the License for the specific language governing permissions
111545Seschrock  * and limitations under the License.
121545Seschrock  *
131545Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
141545Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151545Seschrock  * If applicable, add the following below this CDDL HEADER, with the
161545Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
171545Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
181545Seschrock  *
191545Seschrock  * CDDL HEADER END
201545Seschrock  */
211545Seschrock 
221545Seschrock /*
231545Seschrock  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
241545Seschrock  * Use is subject to license terms.
251545Seschrock  */
261545Seschrock 
271545Seschrock #pragma ident	"%Z%%M%	%I%	%E% SMI"
281545Seschrock 
291545Seschrock #include <assert.h>
301545Seschrock #include <errno.h>
311545Seschrock #include <fcntl.h>
321545Seschrock #include <gelf.h>
331545Seschrock #include <libelf.h>
341545Seschrock #include <stdlib.h>
351545Seschrock #include <string.h>
361545Seschrock #include <unistd.h>
371545Seschrock 
381545Seschrock #include <sys/fcntl.h>
391545Seschrock #include <sys/stat.h>
401545Seschrock 
411545Seschrock #include "dis_target.h"
421545Seschrock #include "dis_util.h"
431545Seschrock 
441545Seschrock /*
451545Seschrock  * Standard ELF disassembler target.
461545Seschrock  *
471545Seschrock  * We only support disassembly of ELF files, though this target interface could
481545Seschrock  * be extended in the future.  Each basic type (target, func, section) contains
491545Seschrock  * enough information to uniquely identify the location within the file.  The
501545Seschrock  * interfaces use libelf(3LIB) to do the actual processing of the file.
511545Seschrock  */
521545Seschrock 
531545Seschrock /*
541545Seschrock  * Symbol table entry type.  We maintain our own symbol table sorted by address,
551545Seschrock  * with the symbol name already resolved against the ELF symbol table.
561545Seschrock  */
571545Seschrock typedef struct sym_entry {
581545Seschrock 	GElf_Sym	se_sym;		/* value of symbol */
591545Seschrock 	char		*se_name;	/* name of symbol */
601545Seschrock 	int		se_shndx;	/* section where symbol is located */
611545Seschrock } sym_entry_t;
621545Seschrock 
631545Seschrock /*
641545Seschrock  * Target data structure.  This structure keeps track of the ELF file
651545Seschrock  * information, a few bits of pre-processed section index information, and
661545Seschrock  * sorted versions of the symbol table.  We also keep track of the last symbol
671545Seschrock  * looked up, as the majority of lookups remain within the same symbol.
681545Seschrock  */
691545Seschrock struct dis_tgt {
701545Seschrock 	Elf		*dt_elf;	/* libelf handle */
711545Seschrock 	Elf		*dt_elf_root;	/* main libelf handle (for archives) */
721545Seschrock 	const char	*dt_filename;	/* name of file */
731545Seschrock 	int		dt_fd;		/* underlying file descriptor */
741545Seschrock 	size_t		dt_shstrndx;	/* section index of .shstrtab */
751545Seschrock 	size_t		dt_symidx;	/* section index of symbol table */
761545Seschrock 	sym_entry_t	*dt_symcache;	/* last symbol looked up */
771545Seschrock 	sym_entry_t	*dt_symtab;	/* sorted symbol table */
781545Seschrock 	int		dt_symcount;	/* # of symbol table entries */
791545Seschrock 	struct dis_tgt	*dt_next;	/* next target (for archives) */
801545Seschrock 	Elf_Arhdr	*dt_arhdr;	/* archive header (for archives) */
811545Seschrock };
821545Seschrock 
831545Seschrock /*
841545Seschrock  * Function data structure.  We resolve the symbol and lookup the associated ELF
851545Seschrock  * data when building this structure.  The offset is calculated based on the
861545Seschrock  * section's starting address.
871545Seschrock  */
881545Seschrock struct dis_func {
891545Seschrock 	sym_entry_t	*df_sym;	/* symbol table reference */
901545Seschrock 	Elf_Data	*df_data;	/* associated ELF data */
911545Seschrock 	size_t		df_offset;	/* offset within data */
921545Seschrock };
931545Seschrock 
941545Seschrock /*
951545Seschrock  * Section data structure.  We store the entire section header so that we can
961545Seschrock  * determine some properties (such as whether or not it contains text) after
971545Seschrock  * building the structure.
981545Seschrock  */
991545Seschrock struct dis_scn {
1001545Seschrock 	GElf_Shdr	ds_shdr;
1011545Seschrock 	const char	*ds_name;
1021545Seschrock 	Elf_Data	*ds_data;
1031545Seschrock };
1041545Seschrock 
1051545Seschrock /* Lifted from Psymtab.c */
1061545Seschrock #define	DATA_TYPES      \
1071545Seschrock 	((1 << STT_OBJECT) | (1 << STT_FUNC) | \
1081545Seschrock 	(1 << STT_COMMON) | (1 << STT_TLS))
1091545Seschrock #define	IS_DATA_TYPE(tp)	(((1 << (tp)) & DATA_TYPES) != 0)
1101545Seschrock 
1111545Seschrock /*
1121545Seschrock  * Pick out the best symbol to used based on the sections available in the
1131545Seschrock  * target.  We prefer SHT_SYMTAB over SHT_DYNSYM.
1141545Seschrock  */
1151545Seschrock /* ARGSUSED */
1161545Seschrock static void
1171545Seschrock get_symtab(dis_tgt_t *tgt, dis_scn_t *scn, void *data)
1181545Seschrock {
1191545Seschrock 	int *index = data;
1201545Seschrock 
1211545Seschrock 	*index += 1;
1221545Seschrock 
1231545Seschrock 	/*
1241545Seschrock 	 * Prefer SHT_SYMTAB over SHT_DYNSYM
1251545Seschrock 	 */
1261545Seschrock 	if (scn->ds_shdr.sh_type == SHT_DYNSYM && tgt->dt_symidx == 0)
1271545Seschrock 		tgt->dt_symidx = *index;
1281545Seschrock 	else if (scn->ds_shdr.sh_type == SHT_SYMTAB)
1291545Seschrock 		tgt->dt_symidx = *index;
1301545Seschrock }
1311545Seschrock 
1321545Seschrock static int
1331545Seschrock sym_compare(const void *a, const void *b)
1341545Seschrock {
1351545Seschrock 	const sym_entry_t *syma = a;
1361545Seschrock 	const sym_entry_t *symb = b;
1371545Seschrock 	const char *aname = syma->se_name;
1381545Seschrock 	const char *bname = symb->se_name;
1391545Seschrock 
1401545Seschrock 	if (syma->se_sym.st_value < symb->se_sym.st_value)
1411545Seschrock 		return (-1);
1421545Seschrock 
1431545Seschrock 	if (syma->se_sym.st_value > symb->se_sym.st_value)
1441545Seschrock 		return (1);
1451545Seschrock 
1461545Seschrock 	/*
1471545Seschrock 	 * Prefer functions over non-functions
1481545Seschrock 	 */
1491545Seschrock 	if (GELF_ST_TYPE(syma->se_sym.st_info) !=
1501545Seschrock 	    GELF_ST_TYPE(symb->se_sym.st_info)) {
1511545Seschrock 		if (GELF_ST_TYPE(syma->se_sym.st_info) == STT_FUNC)
1521545Seschrock 			return (-1);
1531545Seschrock 		if (GELF_ST_TYPE(symb->se_sym.st_info) == STT_FUNC)
1541545Seschrock 			return (1);
1551545Seschrock 	}
1561545Seschrock 
1571545Seschrock 	/*
1581545Seschrock 	 * For symbols with the same address and type, we sort them according to
1591545Seschrock 	 * a hierarchy:
1601545Seschrock 	 *
1611545Seschrock 	 * 	1. weak symbols (common name)
1621545Seschrock 	 * 	2. global symbols (external name)
1631545Seschrock 	 * 	3. local symbols
1641545Seschrock 	 */
1651545Seschrock 	if (GELF_ST_BIND(syma->se_sym.st_info) !=
1661545Seschrock 	    GELF_ST_BIND(symb->se_sym.st_info)) {
1671545Seschrock 		if (GELF_ST_BIND(syma->se_sym.st_info) == STB_WEAK)
1681545Seschrock 			return (-1);
1691545Seschrock 		if (GELF_ST_BIND(symb->se_sym.st_info) == STB_WEAK)
1701545Seschrock 			return (1);
1711545Seschrock 
1721545Seschrock 		if (GELF_ST_BIND(syma->se_sym.st_info) == STB_GLOBAL)
1731545Seschrock 			return (-1);
1741545Seschrock 		if (GELF_ST_BIND(symb->se_sym.st_info) == STB_GLOBAL)
1751545Seschrock 			return (1);
1761545Seschrock 	}
1771545Seschrock 
1781545Seschrock 	/*
1791545Seschrock 	 * As a last resort, if we have multiple symbols of the same type at the
1801545Seschrock 	 * same address, prefer the version with the fewest leading underscores.
1811545Seschrock 	 */
1821545Seschrock 	if (aname == NULL)
1831545Seschrock 		return (-1);
1841545Seschrock 	if (bname == NULL)
1851545Seschrock 		return (1);
1861545Seschrock 
1871545Seschrock 	while (*aname == '_' && *bname == '_') {
1881545Seschrock 		aname++;
1891545Seschrock 		bname++;
1901545Seschrock 	}
1911545Seschrock 
1921545Seschrock 	if (*bname == '_')
1931545Seschrock 		return (-1);
1941545Seschrock 	if (*aname == '_')
1951545Seschrock 		return (1);
1961545Seschrock 
1971545Seschrock 	/*
1981545Seschrock 	 * Prefer the symbol with the smaller size.
1991545Seschrock 	 */
2001545Seschrock 	if (syma->se_sym.st_size < symb->se_sym.st_size)
2011545Seschrock 		return (-1);
2021545Seschrock 	if (syma->se_sym.st_size > symb->se_sym.st_size)
2031545Seschrock 		return (1);
2041545Seschrock 
2051545Seschrock 	/*
2061545Seschrock 	 * We really do have two identical symbols for some reason.  Just report
2071545Seschrock 	 * them as equal, and to the lucky one go the spoils.
2081545Seschrock 	 */
2091545Seschrock 	return (0);
2101545Seschrock }
2111545Seschrock 
2121545Seschrock /*
2131545Seschrock  * Construct an optimized symbol table sorted by starting address.
2141545Seschrock  */
2151545Seschrock static void
2161545Seschrock construct_symtab(dis_tgt_t *tgt)
2171545Seschrock {
2181545Seschrock 	Elf_Scn *scn;
2191545Seschrock 	GElf_Shdr shdr;
2201545Seschrock 	Elf_Data *symdata;
2211545Seschrock 	int i;
2221545Seschrock 	GElf_Word *symshndx = NULL;
2231545Seschrock 	int symshndx_size;
2241545Seschrock 	sym_entry_t *sym;
2251545Seschrock 	sym_entry_t *p_symtab = NULL;
2261545Seschrock 	int nsym = 0; /* count of symbols we're not interested in */
2271545Seschrock 
2281545Seschrock 	/*
2291545Seschrock 	 * Find the symshndx section, if any
2301545Seschrock 	 */
2311545Seschrock 	for (scn = elf_nextscn(tgt->dt_elf, NULL); scn != NULL;
2321545Seschrock 	    scn = elf_nextscn(tgt->dt_elf, scn)) {
2331545Seschrock 		if (gelf_getshdr(scn, &shdr) == NULL)
2341545Seschrock 			break;
2351545Seschrock 		if (shdr.sh_type == SHT_SYMTAB_SHNDX &&
2361545Seschrock 		    shdr.sh_link == tgt->dt_symidx) {
2371545Seschrock 			Elf_Data	*data;
2381545Seschrock 
2391545Seschrock 			if ((data = elf_getdata(scn, NULL)) != NULL) {
2401545Seschrock 				symshndx = (GElf_Word *)data->d_buf;
2411545Seschrock 				symshndx_size = data->d_size /
2421545Seschrock 				    sizeof (GElf_Word);
2431545Seschrock 				break;
2441545Seschrock 			}
2451545Seschrock 		}
2461545Seschrock 	}
2471545Seschrock 
2481545Seschrock 	if ((scn = elf_getscn(tgt->dt_elf, tgt->dt_symidx)) == NULL)
2491545Seschrock 		die("%s: failed to get section information", tgt->dt_filename);
2501545Seschrock 	if (gelf_getshdr(scn, &shdr) == NULL)
2511545Seschrock 		die("%s: failed to get section header", tgt->dt_filename);
2521545Seschrock 	if (shdr.sh_entsize == 0)
2531545Seschrock 		die("%s: symbol table has zero size", tgt->dt_filename);
2541545Seschrock 
2551545Seschrock 	if ((symdata = elf_getdata(scn, NULL)) == NULL)
2561545Seschrock 		die("%s: failed to get symbol table", tgt->dt_filename);
2571545Seschrock 
2581545Seschrock 	tgt->dt_symcount = symdata->d_size / gelf_fsize(tgt->dt_elf, ELF_T_SYM,
2591545Seschrock 		1, EV_CURRENT);
2601545Seschrock 
2611545Seschrock 	p_symtab = safe_malloc(tgt->dt_symcount * sizeof (sym_entry_t));
2621545Seschrock 
2631545Seschrock 	for (i = 0, sym = p_symtab; i < tgt->dt_symcount; i++) {
2641545Seschrock 		(void) memset(sym, sizeof (sym_entry_t), 0);
2651545Seschrock 		if (gelf_getsym(symdata, i, &(sym->se_sym)) == NULL) {
2661545Seschrock 			warn("%s: gelf_getsym returned NULL for %d",
2671545Seschrock 				tgt->dt_filename, i);
2681545Seschrock 			nsym++;
2691545Seschrock 			continue;
2701545Seschrock 		}
2711545Seschrock 
2721545Seschrock 		/*
2731545Seschrock 		 * We're only interested in data symbols.
2741545Seschrock 		 */
2751545Seschrock 		if (!IS_DATA_TYPE(GELF_ST_TYPE(sym->se_sym.st_info))) {
2761545Seschrock 			nsym++;
2771545Seschrock 			continue;
2781545Seschrock 		}
2791545Seschrock 
2801545Seschrock 		if (sym->se_sym.st_shndx == SHN_XINDEX && symshndx != NULL) {
2811545Seschrock 			if (i > symshndx_size) {
2821545Seschrock 				warn("%s: bad SHNX_XINDEX %d",
2831545Seschrock 					tgt->dt_filename, i);
2841545Seschrock 				sym->se_shndx = -1;
2851545Seschrock 			} else {
2861545Seschrock 				sym->se_shndx = symshndx[i];
2871545Seschrock 			}
2881545Seschrock 		} else {
2891545Seschrock 			sym->se_shndx = sym->se_sym.st_shndx;
2901545Seschrock 		}
2911545Seschrock 
2921545Seschrock 		if ((sym->se_name = elf_strptr(tgt->dt_elf, shdr.sh_link,
2931545Seschrock 		    (size_t)sym->se_sym.st_name)) == NULL) {
2941545Seschrock 			warn("%s: failed to lookup symbol %d name",
2951545Seschrock 				tgt->dt_filename, i);
2961545Seschrock 			nsym++;
2971545Seschrock 			continue;
2981545Seschrock 		}
2991545Seschrock 
3001545Seschrock 		sym++;
3011545Seschrock 	}
3021545Seschrock 
3031545Seschrock 	tgt->dt_symcount -= nsym;
3041545Seschrock 	tgt->dt_symtab = realloc(p_symtab,
3051545Seschrock 				tgt->dt_symcount * sizeof (sym_entry_t));
3061545Seschrock 
3071545Seschrock 	qsort(tgt->dt_symtab, tgt->dt_symcount, sizeof (sym_entry_t),
3081545Seschrock 	    sym_compare);
3091545Seschrock }
3101545Seschrock 
3111545Seschrock /*
3121545Seschrock  * Create a target backed by an ELF file.
3131545Seschrock  */
3141545Seschrock dis_tgt_t *
3151545Seschrock dis_tgt_create(const char *file)
3161545Seschrock {
3171545Seschrock 	dis_tgt_t *tgt, *current;
3181545Seschrock 	int idx;
3191545Seschrock 	Elf *elf;
3201545Seschrock 	GElf_Ehdr ehdr;
3211545Seschrock 	Elf_Arhdr *arhdr = NULL;
3221545Seschrock 	int cmd;
3231545Seschrock 
3241545Seschrock 	if (elf_version(EV_CURRENT) == EV_NONE)
3251545Seschrock 		die("libelf(3ELF) out of date");
3261545Seschrock 
3271545Seschrock 	tgt = safe_malloc(sizeof (dis_tgt_t));
3281545Seschrock 
3291545Seschrock 	if ((tgt->dt_fd = open(file, O_RDONLY)) < 0) {
3301545Seschrock 		warn("%s: failed opening file, reason: %s", file,
3311545Seschrock 			strerror(errno));
3321545Seschrock 		free(tgt);
3331545Seschrock 		return (NULL);
3341545Seschrock 	}
3351545Seschrock 
3361545Seschrock 	if ((tgt->dt_elf_root =
3371545Seschrock 	    elf_begin(tgt->dt_fd, ELF_C_READ, NULL)) == NULL) {
3381545Seschrock 		warn("%s: invalid or corrupt ELF file", file);
3391545Seschrock 		dis_tgt_destroy(tgt);
3401545Seschrock 		return (NULL);
3411545Seschrock 	}
3421545Seschrock 
3431545Seschrock 	current = tgt;
3441545Seschrock 	cmd = ELF_C_READ;
3451545Seschrock 	while ((elf = elf_begin(tgt->dt_fd, cmd, tgt->dt_elf_root)) != NULL) {
3461545Seschrock 
3471545Seschrock 		if (elf_kind(tgt->dt_elf_root) == ELF_K_AR &&
3481545Seschrock 		    (arhdr = elf_getarhdr(elf)) == NULL) {
3491545Seschrock 			warn("%s: malformed archive", file);
3501545Seschrock 			dis_tgt_destroy(tgt);
3511545Seschrock 			return (NULL);
3521545Seschrock 		}
3531545Seschrock 
3541545Seschrock 		/*
3551545Seschrock 		 * Make sure that this Elf file is sane
3561545Seschrock 		 */
3571545Seschrock 		if (gelf_getehdr(elf, &ehdr) == NULL) {
3581545Seschrock 			if (arhdr != NULL) {
3591545Seschrock 				/*
3601545Seschrock 				 * For archives, we drive on in the face of bad
3611545Seschrock 				 * members.  The "/" and "//" members are
3621545Seschrock 				 * special, and should be silently ignored.
3631545Seschrock 				 */
3641545Seschrock 				if (strcmp(arhdr->ar_name, "/") != 0 &&
3651545Seschrock 				    strcmp(arhdr->ar_name, "//") != 0)
3661545Seschrock 					warn("%s[%s]: invalid file type",
3671545Seschrock 					    file, arhdr->ar_name);
3681545Seschrock 				cmd = elf_next(elf);
3691545Seschrock 				(void) elf_end(elf);
3701545Seschrock 				continue;
3711545Seschrock 			}
3721545Seschrock 
3731545Seschrock 			warn("%s: invalid file type", file);
3741545Seschrock 			dis_tgt_destroy(tgt);
3751545Seschrock 			return (NULL);
3761545Seschrock 		}
3771545Seschrock 
3781545Seschrock 		/*
3791545Seschrock 		 * If we're seeing a new Elf object, then we have an
3801545Seschrock 		 * archive. In this case, we create a new target, and chain it
3811545Seschrock 		 * off the master target.  We can later iterate over these
3821545Seschrock 		 * targets using dis_tgt_next().
3831545Seschrock 		 */
3841545Seschrock 		if (current->dt_elf != NULL) {
3851545Seschrock 			dis_tgt_t *next = safe_malloc(sizeof (dis_tgt_t));
3861545Seschrock 			next->dt_elf_root = tgt->dt_elf_root;
3871545Seschrock 			next->dt_fd = -1;
3881545Seschrock 			current->dt_next = next;
3891545Seschrock 			current = next;
3901545Seschrock 		}
3911545Seschrock 		current->dt_elf = elf;
3921545Seschrock 		current->dt_arhdr = arhdr;
3931545Seschrock 
3941545Seschrock 		if (elf_getshstrndx(elf, &current->dt_shstrndx) == -1) {
3951545Seschrock 			warn("%s: failed to get section string table for "
3961545Seschrock 			    "file", file);
3971545Seschrock 			dis_tgt_destroy(tgt);
3981545Seschrock 			return (NULL);
3991545Seschrock 		}
4001545Seschrock 
4011545Seschrock 		idx = 0;
4021545Seschrock 		dis_tgt_section_iter(current, get_symtab, &idx);
4031545Seschrock 
4041545Seschrock 		if (current->dt_symidx != 0)
4051545Seschrock 			construct_symtab(current);
4061545Seschrock 
4071545Seschrock 		current->dt_filename = file;
4081545Seschrock 
4091545Seschrock 		cmd = elf_next(elf);
4101545Seschrock 	}
4111545Seschrock 
4121545Seschrock 	/*
4131545Seschrock 	 * Final sanity check.  If we had an archive with no members, then bail
4141545Seschrock 	 * out with a nice message.
4151545Seschrock 	 */
4161545Seschrock 	if (tgt->dt_elf == NULL) {
4171545Seschrock 		warn("%s: empty archive\n", file);
4181545Seschrock 		dis_tgt_destroy(tgt);
4191545Seschrock 		return (NULL);
4201545Seschrock 	}
4211545Seschrock 
4221545Seschrock 	return (tgt);
4231545Seschrock }
4241545Seschrock 
4251545Seschrock /*
4261545Seschrock  * Return the filename associated with the target.
4271545Seschrock  */
4281545Seschrock const char *
4291545Seschrock dis_tgt_name(dis_tgt_t *tgt)
4301545Seschrock {
4311545Seschrock 	return (tgt->dt_filename);
4321545Seschrock }
4331545Seschrock 
4341545Seschrock /*
4351545Seschrock  * Return the archive member name, if any.
4361545Seschrock  */
4371545Seschrock const char *
4381545Seschrock dis_tgt_member(dis_tgt_t *tgt)
4391545Seschrock {
4401545Seschrock 	if (tgt->dt_arhdr)
4411545Seschrock 		return (tgt->dt_arhdr->ar_name);
4421545Seschrock 	else
4431545Seschrock 		return (NULL);
4441545Seschrock }
4451545Seschrock 
4461545Seschrock /*
4471545Seschrock  * Return the Elf_Ehdr associated with this target.  Needed to determine which
4481545Seschrock  * disassembler to use.
4491545Seschrock  */
4501545Seschrock void
4511545Seschrock dis_tgt_ehdr(dis_tgt_t *tgt, GElf_Ehdr *ehdr)
4521545Seschrock {
4531545Seschrock 	(void) gelf_getehdr(tgt->dt_elf, ehdr);
4541545Seschrock }
4551545Seschrock 
4561545Seschrock /*
4571545Seschrock  * Return the next target in the list, if this is an archive.
4581545Seschrock  */
4591545Seschrock dis_tgt_t *
4601545Seschrock dis_tgt_next(dis_tgt_t *tgt)
4611545Seschrock {
4621545Seschrock 	return (tgt->dt_next);
4631545Seschrock }
4641545Seschrock 
4651545Seschrock /*
4661545Seschrock  * Destroy a target and free up any associated memory.
4671545Seschrock  */
4681545Seschrock void
4691545Seschrock dis_tgt_destroy(dis_tgt_t *tgt)
4701545Seschrock {
4711545Seschrock 	dis_tgt_t *current, *next;
4721545Seschrock 
4731545Seschrock 	current = tgt->dt_next;
4741545Seschrock 	while (current != NULL) {
4751545Seschrock 		next = current->dt_next;
4761545Seschrock 		if (current->dt_elf)
4771545Seschrock 			(void) elf_end(current->dt_elf);
4781545Seschrock 		if (current->dt_symtab)
4791545Seschrock 			free(current->dt_symtab);
4801545Seschrock 		free(current);
4811545Seschrock 		current = next;
4821545Seschrock 	}
4831545Seschrock 
4841545Seschrock 	if (tgt->dt_elf)
4851545Seschrock 		(void) elf_end(tgt->dt_elf);
4861545Seschrock 	if (tgt->dt_elf_root)
4871545Seschrock 		(void) elf_end(tgt->dt_elf_root);
4881545Seschrock 
4891545Seschrock 	if (tgt->dt_symtab)
4901545Seschrock 		free(tgt->dt_symtab);
4911545Seschrock 
4921545Seschrock 	free(tgt);
4931545Seschrock }
4941545Seschrock 
4951545Seschrock /*
4961545Seschrock  * Given an address, returns the name of the corresponding symbol, as well as
4971545Seschrock  * the offset within that symbol.  If no matching symbol is found, then NULL is
4981545Seschrock  * returned.
4991545Seschrock  *
5001545Seschrock  * If 'cache_result' is specified, then we keep track of the resulting symbol.
5011545Seschrock  * This cached result is consulted first on subsequent lookups in order to avoid
5021545Seschrock  * unecessary lookups.  This flag should be used for resolving the current PC,
5031545Seschrock  * as the majority of addresses stay within the current function.
5041545Seschrock  */
5051545Seschrock const char *
5061545Seschrock dis_tgt_lookup(dis_tgt_t *tgt, uint64_t addr, off_t *offset, int cache_result,
5071545Seschrock     size_t *size, int *isfunc)
5081545Seschrock {
5091545Seschrock 	int lo, hi, mid;
5101545Seschrock 	sym_entry_t *sym, *osym, *match;
5111545Seschrock 	int found;
5121545Seschrock 
5131545Seschrock 	if (tgt->dt_symcache != NULL &&
5141545Seschrock 	    addr >= tgt->dt_symcache->se_sym.st_value &&
5151545Seschrock 	    addr < tgt->dt_symcache->se_sym.st_value +
5161545Seschrock 	    tgt->dt_symcache->se_sym.st_size) {
5171545Seschrock 		*offset = addr - tgt->dt_symcache->se_sym.st_value;
5181545Seschrock 		*size = tgt->dt_symcache->se_sym.st_size;
5191545Seschrock 		return (tgt->dt_symcache->se_name);
5201545Seschrock 	}
5211545Seschrock 
5221545Seschrock 	lo = 0;
5231545Seschrock 	hi = (tgt->dt_symcount - 1);
5241545Seschrock 	found = 0;
5251545Seschrock 	match = osym = NULL;
5261545Seschrock 	while (lo <= hi) {
5271545Seschrock 		mid = (lo + hi) / 2;
5281545Seschrock 
5291545Seschrock 		sym = &tgt->dt_symtab[mid];
5301545Seschrock 
5311545Seschrock 		if (addr >= sym->se_sym.st_value &&
5321545Seschrock 		    addr < sym->se_sym.st_value + sym->se_sym.st_size &&
5331545Seschrock 		    (!found || sym->se_sym.st_value > osym->se_sym.st_value)) {
5341545Seschrock 			osym = sym;
5351545Seschrock 			found = 1;
5361545Seschrock 		} else if (addr == sym->se_sym.st_value) {
5371545Seschrock 			/*
5381545Seschrock 			 * Particularly for .plt objects, it's possible to have
5391545Seschrock 			 * a zero sized object.  We want to return this, but we
5401545Seschrock 			 * want it to be a last resort.
5411545Seschrock 			 */
5421545Seschrock 			match = sym;
5431545Seschrock 		}
5441545Seschrock 
5451545Seschrock 		if (addr < sym->se_sym.st_value)
5461545Seschrock 			hi = mid - 1;
5471545Seschrock 		else
5481545Seschrock 			lo = mid + 1;
5491545Seschrock 	}
5501545Seschrock 
5511545Seschrock 	if (!found) {
5521545Seschrock 		if (match)
5531545Seschrock 			osym = match;
5541545Seschrock 		else
5551545Seschrock 			return (NULL);
5561545Seschrock 	}
5571545Seschrock 
5581545Seschrock 	/*
5591545Seschrock 	 * Walk backwards to find the best match.
5601545Seschrock 	 */
5611545Seschrock 	do {
5621545Seschrock 		sym = osym;
5631545Seschrock 
5641545Seschrock 		if (osym == tgt->dt_symtab)
5651545Seschrock 			break;
5661545Seschrock 
5671545Seschrock 		osym = osym - 1;
5681545Seschrock 	} while ((sym->se_sym.st_value == osym->se_sym.st_value) &&
5691545Seschrock 		(addr >= osym->se_sym.st_value) &&
5701545Seschrock 		(addr < osym->se_sym.st_value + osym->se_sym.st_size));
5711545Seschrock 
5721545Seschrock 	if (cache_result)
5731545Seschrock 		tgt->dt_symcache = sym;
5741545Seschrock 
5751545Seschrock 	*offset = addr - sym->se_sym.st_value;
5761545Seschrock 	*size = sym->se_sym.st_size;
5771545Seschrock 	if (isfunc)
5781545Seschrock 		*isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) == STT_FUNC);
5791545Seschrock 
5801545Seschrock 	return (sym->se_name);
5811545Seschrock }
5821545Seschrock 
5831545Seschrock /*
5841545Seschrock  * Given an address, return the starting offset of the next symbol in the file.
5851545Seschrock  * Relies on the fact that this is only used when we encounter a bad instruction
5861545Seschrock  * in the input stream, so we know that the last symbol looked up will be in the
5871545Seschrock  * cache.
5881545Seschrock  */
5891545Seschrock off_t
5901545Seschrock dis_tgt_next_symbol(dis_tgt_t *tgt, uint64_t addr)
5911545Seschrock {
5921545Seschrock 	sym_entry_t *sym = tgt->dt_symcache;
5931545Seschrock 	uint64_t start;
5941545Seschrock 
5951545Seschrock 	/* make sure the cached symbol and address are valid */
5961545Seschrock 	if (sym == NULL || addr < sym->se_sym.st_value ||
5971545Seschrock 	    addr >= sym->se_sym.st_value + sym->se_sym.st_size)
5981545Seschrock 		return (0);
5991545Seschrock 
6001545Seschrock 	start = sym->se_sym.st_value;
6011545Seschrock 
6021545Seschrock 	/* find the next symbol */
6031545Seschrock 	while (sym != tgt->dt_symtab + tgt->dt_symcount &&
6041545Seschrock 	    sym->se_sym.st_value == start)
6051545Seschrock 		sym++;
6061545Seschrock 
6071545Seschrock 	return (sym->se_sym.st_value - addr);
6081545Seschrock }
6091545Seschrock 
6101545Seschrock /*
6111545Seschrock  * Iterate over all sections in the target, executing the given callback for
6121545Seschrock  * each.
6131545Seschrock  */
6141545Seschrock void
6151545Seschrock dis_tgt_section_iter(dis_tgt_t *tgt, section_iter_f func, void *data)
6161545Seschrock {
6171545Seschrock 	dis_scn_t sdata;
6181545Seschrock 	Elf_Scn *scn;
6191545Seschrock 	int idx;
6201545Seschrock 
6211545Seschrock 	for (scn = elf_nextscn(tgt->dt_elf, NULL), idx = 1; scn != NULL;
6221545Seschrock 	    scn = elf_nextscn(tgt->dt_elf, scn), idx++) {
6231545Seschrock 
6241545Seschrock 		if (gelf_getshdr(scn, &sdata.ds_shdr) == NULL) {
6251545Seschrock 			warn("%s: failed to get section %d header",
6261545Seschrock 			    tgt->dt_filename, idx);
6271545Seschrock 			continue;
6281545Seschrock 		}
6291545Seschrock 
6301545Seschrock 		if ((sdata.ds_name = elf_strptr(tgt->dt_elf, tgt->dt_shstrndx,
6311545Seschrock 		    sdata.ds_shdr.sh_name)) == NULL) {
6321545Seschrock 			warn("%s: failed to get section %d name",
6331545Seschrock 			    tgt->dt_filename, idx);
6341545Seschrock 			continue;
6351545Seschrock 		}
6361545Seschrock 
6371545Seschrock 		if ((sdata.ds_data = elf_getdata(scn, NULL)) == NULL) {
6381545Seschrock 			warn("%s: failed to get data for section '%s'",
6391545Seschrock 			    tgt->dt_filename, sdata.ds_name);
6401545Seschrock 			continue;
6411545Seschrock 		}
6421545Seschrock 
6431545Seschrock 		func(tgt, &sdata, data);
6441545Seschrock 	}
6451545Seschrock }
6461545Seschrock 
6471545Seschrock /*
6481545Seschrock  * Return 1 if the given section contains text, 0 otherwise.
6491545Seschrock  */
6501545Seschrock int
6511545Seschrock dis_section_istext(dis_scn_t *scn)
6521545Seschrock {
6531545Seschrock 	return ((scn->ds_shdr.sh_type == SHT_PROGBITS) &&
6541545Seschrock 	    (scn->ds_shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR)));
6551545Seschrock }
6561545Seschrock 
6571545Seschrock /*
6581545Seschrock  * Return a pointer to the section data.
6591545Seschrock  */
6601545Seschrock void *
6611545Seschrock dis_section_data(dis_scn_t *scn)
6621545Seschrock {
6631545Seschrock 	return (scn->ds_data->d_buf);
6641545Seschrock }
6651545Seschrock 
6661545Seschrock /*
6671545Seschrock  * Return the size of the section data.
6681545Seschrock  */
6691545Seschrock size_t
6701545Seschrock dis_section_size(dis_scn_t *scn)
6711545Seschrock {
6721545Seschrock 	return (scn->ds_data->d_size);
6731545Seschrock }
6741545Seschrock 
6751545Seschrock /*
6761545Seschrock  * Return the address for the given section.
6771545Seschrock  */
6781545Seschrock uint64_t
6791545Seschrock dis_section_addr(dis_scn_t *scn)
6801545Seschrock {
6811545Seschrock 	return (scn->ds_shdr.sh_addr);
6821545Seschrock }
6831545Seschrock 
6841545Seschrock /*
6851545Seschrock  * Return the name of the current section.
6861545Seschrock  */
6871545Seschrock const char *
6881545Seschrock dis_section_name(dis_scn_t *scn)
6891545Seschrock {
6901545Seschrock 	return (scn->ds_name);
6911545Seschrock }
6921545Seschrock 
6931545Seschrock /*
6941545Seschrock  * Create an allocated copy of the given section
6951545Seschrock  */
6961545Seschrock dis_scn_t *
6971545Seschrock dis_section_copy(dis_scn_t *scn)
6981545Seschrock {
6991545Seschrock 	dis_scn_t *new;
7001545Seschrock 
7011545Seschrock 	new = safe_malloc(sizeof (dis_scn_t));
7021545Seschrock 	(void) memcpy(new, scn, sizeof (dis_scn_t));
7031545Seschrock 
7041545Seschrock 	return (new);
7051545Seschrock }
7061545Seschrock 
7071545Seschrock /*
7081545Seschrock  * Free section memory
7091545Seschrock  */
7101545Seschrock void
7111545Seschrock dis_section_free(dis_scn_t *scn)
7121545Seschrock {
7131545Seschrock 	free(scn);
7141545Seschrock }
7151545Seschrock 
7161545Seschrock /*
7171545Seschrock  * Iterate over all functions in the target, executing the given callback for
7181545Seschrock  * each one.
7191545Seschrock  */
7201545Seschrock void
7211545Seschrock dis_tgt_function_iter(dis_tgt_t *tgt, function_iter_f func, void *data)
7221545Seschrock {
7231545Seschrock 	int i;
7241545Seschrock 	sym_entry_t *sym;
7251545Seschrock 	dis_func_t df;
7261545Seschrock 	Elf_Scn *scn;
7271545Seschrock 	GElf_Shdr	shdr;
7281545Seschrock 
7291545Seschrock 	for (i = 0, sym = tgt->dt_symtab; i < tgt->dt_symcount; i++, sym++) {
7301545Seschrock 
7311545Seschrock 		/* ignore non-functions */
7321545Seschrock 		if ((GELF_ST_TYPE(sym->se_sym.st_info) != STT_FUNC) ||
733*2355Srie 		    (sym->se_name == NULL) ||
734*2355Srie 		    (sym->se_sym.st_size == 0) ||
735*2355Srie 		    (sym->se_shndx >= SHN_LORESERVE))
7361545Seschrock 			continue;
7371545Seschrock 
7381545Seschrock 		/* get the ELF data associated with this function */
7391545Seschrock 		if ((scn = elf_getscn(tgt->dt_elf, sym->se_shndx)) == NULL ||
7401545Seschrock 		    gelf_getshdr(scn, &shdr) == NULL ||
7411545Seschrock 		    (df.df_data = elf_getdata(scn, NULL)) == NULL ||
7421545Seschrock 		    df.df_data->d_size == 0) {
7431545Seschrock 			warn("%s: failed to read section %d",
7441545Seschrock 			    tgt->dt_filename, sym->se_shndx);
7451545Seschrock 			continue;
7461545Seschrock 		}
7471545Seschrock 
7481545Seschrock 		/*
7491545Seschrock 		 * Verify that the address lies within the section that we think
7501545Seschrock 		 * it does.
7511545Seschrock 		 */
7521545Seschrock 		if (sym->se_sym.st_value < shdr.sh_addr ||
7531545Seschrock 		    (sym->se_sym.st_value + sym->se_sym.st_size) >
7541545Seschrock 		    (shdr.sh_addr + shdr.sh_size)) {
7551545Seschrock 			warn("%s: bad section %d for address %p",
7561545Seschrock 				tgt->dt_filename, sym->se_sym.st_shndx,
7571545Seschrock 				sym->se_sym.st_value);
7581545Seschrock 			continue;
7591545Seschrock 		}
7601545Seschrock 
7611545Seschrock 		df.df_sym = sym;
7621545Seschrock 		df.df_offset = sym->se_sym.st_value - shdr.sh_addr;
7631545Seschrock 
7641545Seschrock 		func(tgt, &df, data);
7651545Seschrock 	}
7661545Seschrock }
7671545Seschrock 
7681545Seschrock /*
7691545Seschrock  * Return the data associated with a given function.
7701545Seschrock  */
7711545Seschrock void *
7721545Seschrock dis_function_data(dis_func_t *func)
7731545Seschrock {
7741545Seschrock 	return ((char *)func->df_data->d_buf + func->df_offset);
7751545Seschrock }
7761545Seschrock 
7771545Seschrock /*
7781545Seschrock  * Return the size of a function.
7791545Seschrock  */
7801545Seschrock size_t
7811545Seschrock dis_function_size(dis_func_t *func)
7821545Seschrock {
7831545Seschrock 	return (func->df_sym->se_sym.st_size);
7841545Seschrock }
7851545Seschrock 
7861545Seschrock /*
7871545Seschrock  * Return the address of a function.
7881545Seschrock  */
7891545Seschrock uint64_t
7901545Seschrock dis_function_addr(dis_func_t *func)
7911545Seschrock {
7921545Seschrock 	return (func->df_sym->se_sym.st_value);
7931545Seschrock }
7941545Seschrock 
7951545Seschrock /*
7961545Seschrock  * Return the name of the function
7971545Seschrock  */
7981545Seschrock const char *
7991545Seschrock dis_function_name(dis_func_t *func)
8001545Seschrock {
8011545Seschrock 	return (func->df_sym->se_name);
8021545Seschrock }
8031545Seschrock 
8041545Seschrock /*
8051545Seschrock  * Return a copy of a function.
8061545Seschrock  */
8071545Seschrock dis_func_t *
8081545Seschrock dis_function_copy(dis_func_t *func)
8091545Seschrock {
8101545Seschrock 	dis_func_t *new;
8111545Seschrock 
8121545Seschrock 	new = safe_malloc(sizeof (dis_func_t));
8131545Seschrock 	(void) memcpy(new, func, sizeof (dis_func_t));
8141545Seschrock 
8151545Seschrock 	return (new);
8161545Seschrock }
8171545Seschrock 
8181545Seschrock /*
8191545Seschrock  * Free function memory
8201545Seschrock  */
8211545Seschrock void
8221545Seschrock dis_function_free(dis_func_t *func)
8231545Seschrock {
8241545Seschrock 	free(func);
8251545Seschrock }
826