Home | History | Annotate | Download | only in dis
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 #include <assert.h>
     30 #include <errno.h>
     31 #include <fcntl.h>
     32 #include <gelf.h>
     33 #include <libelf.h>
     34 #include <stdlib.h>
     35 #include <string.h>
     36 #include <unistd.h>
     37 
     38 #include <sys/fcntl.h>
     39 #include <sys/stat.h>
     40 
     41 #include "dis_target.h"
     42 #include "dis_util.h"
     43 
     44 /*
     45  * Standard ELF disassembler target.
     46  *
     47  * We only support disassembly of ELF files, though this target interface could
     48  * be extended in the future.  Each basic type (target, func, section) contains
     49  * enough information to uniquely identify the location within the file.  The
     50  * interfaces use libelf(3LIB) to do the actual processing of the file.
     51  */
     52 
     53 /*
     54  * Symbol table entry type.  We maintain our own symbol table sorted by address,
     55  * with the symbol name already resolved against the ELF symbol table.
     56  */
     57 typedef struct sym_entry {
     58 	GElf_Sym	se_sym;		/* value of symbol */
     59 	char		*se_name;	/* name of symbol */
     60 	int		se_shndx;	/* section where symbol is located */
     61 } sym_entry_t;
     62 
     63 /*
     64  * Target data structure.  This structure keeps track of the ELF file
     65  * information, a few bits of pre-processed section index information, and
     66  * sorted versions of the symbol table.  We also keep track of the last symbol
     67  * looked up, as the majority of lookups remain within the same symbol.
     68  */
     69 struct dis_tgt {
     70 	Elf		*dt_elf;	/* libelf handle */
     71 	Elf		*dt_elf_root;	/* main libelf handle (for archives) */
     72 	const char	*dt_filename;	/* name of file */
     73 	int		dt_fd;		/* underlying file descriptor */
     74 	size_t		dt_shstrndx;	/* section index of .shstrtab */
     75 	size_t		dt_symidx;	/* section index of symbol table */
     76 	sym_entry_t	*dt_symcache;	/* last symbol looked up */
     77 	sym_entry_t	*dt_symtab;	/* sorted symbol table */
     78 	int		dt_symcount;	/* # of symbol table entries */
     79 	struct dis_tgt	*dt_next;	/* next target (for archives) */
     80 	Elf_Arhdr	*dt_arhdr;	/* archive header (for archives) */
     81 };
     82 
     83 /*
     84  * Function data structure.  We resolve the symbol and lookup the associated ELF
     85  * data when building this structure.  The offset is calculated based on the
     86  * section's starting address.
     87  */
     88 struct dis_func {
     89 	sym_entry_t	*df_sym;	/* symbol table reference */
     90 	Elf_Data	*df_data;	/* associated ELF data */
     91 	size_t		df_offset;	/* offset within data */
     92 };
     93 
     94 /*
     95  * Section data structure.  We store the entire section header so that we can
     96  * determine some properties (such as whether or not it contains text) after
     97  * building the structure.
     98  */
     99 struct dis_scn {
    100 	GElf_Shdr	ds_shdr;
    101 	const char	*ds_name;
    102 	Elf_Data	*ds_data;
    103 };
    104 
    105 /* Lifted from Psymtab.c */
    106 #define	DATA_TYPES      \
    107 	((1 << STT_OBJECT) | (1 << STT_FUNC) | \
    108 	(1 << STT_COMMON) | (1 << STT_TLS))
    109 #define	IS_DATA_TYPE(tp)	(((1 << (tp)) & DATA_TYPES) != 0)
    110 
    111 /*
    112  * Pick out the best symbol to used based on the sections available in the
    113  * target.  We prefer SHT_SYMTAB over SHT_DYNSYM.
    114  */
    115 /* ARGSUSED */
    116 static void
    117 get_symtab(dis_tgt_t *tgt, dis_scn_t *scn, void *data)
    118 {
    119 	int *index = data;
    120 
    121 	*index += 1;
    122 
    123 	/*
    124 	 * Prefer SHT_SYMTAB over SHT_DYNSYM
    125 	 */
    126 	if (scn->ds_shdr.sh_type == SHT_DYNSYM && tgt->dt_symidx == 0)
    127 		tgt->dt_symidx = *index;
    128 	else if (scn->ds_shdr.sh_type == SHT_SYMTAB)
    129 		tgt->dt_symidx = *index;
    130 }
    131 
    132 static int
    133 sym_compare(const void *a, const void *b)
    134 {
    135 	const sym_entry_t *syma = a;
    136 	const sym_entry_t *symb = b;
    137 	const char *aname = syma->se_name;
    138 	const char *bname = symb->se_name;
    139 
    140 	if (syma->se_sym.st_value < symb->se_sym.st_value)
    141 		return (-1);
    142 
    143 	if (syma->se_sym.st_value > symb->se_sym.st_value)
    144 		return (1);
    145 
    146 	/*
    147 	 * Prefer functions over non-functions
    148 	 */
    149 	if (GELF_ST_TYPE(syma->se_sym.st_info) !=
    150 	    GELF_ST_TYPE(symb->se_sym.st_info)) {
    151 		if (GELF_ST_TYPE(syma->se_sym.st_info) == STT_FUNC)
    152 			return (-1);
    153 		if (GELF_ST_TYPE(symb->se_sym.st_info) == STT_FUNC)
    154 			return (1);
    155 	}
    156 
    157 	/*
    158 	 * For symbols with the same address and type, we sort them according to
    159 	 * a hierarchy:
    160 	 *
    161 	 * 	1. weak symbols (common name)
    162 	 * 	2. global symbols (external name)
    163 	 * 	3. local symbols
    164 	 */
    165 	if (GELF_ST_BIND(syma->se_sym.st_info) !=
    166 	    GELF_ST_BIND(symb->se_sym.st_info)) {
    167 		if (GELF_ST_BIND(syma->se_sym.st_info) == STB_WEAK)
    168 			return (-1);
    169 		if (GELF_ST_BIND(symb->se_sym.st_info) == STB_WEAK)
    170 			return (1);
    171 
    172 		if (GELF_ST_BIND(syma->se_sym.st_info) == STB_GLOBAL)
    173 			return (-1);
    174 		if (GELF_ST_BIND(symb->se_sym.st_info) == STB_GLOBAL)
    175 			return (1);
    176 	}
    177 
    178 	/*
    179 	 * As a last resort, if we have multiple symbols of the same type at the
    180 	 * same address, prefer the version with the fewest leading underscores.
    181 	 */
    182 	if (aname == NULL)
    183 		return (-1);
    184 	if (bname == NULL)
    185 		return (1);
    186 
    187 	while (*aname == '_' && *bname == '_') {
    188 		aname++;
    189 		bname++;
    190 	}
    191 
    192 	if (*bname == '_')
    193 		return (-1);
    194 	if (*aname == '_')
    195 		return (1);
    196 
    197 	/*
    198 	 * Prefer the symbol with the smaller size.
    199 	 */
    200 	if (syma->se_sym.st_size < symb->se_sym.st_size)
    201 		return (-1);
    202 	if (syma->se_sym.st_size > symb->se_sym.st_size)
    203 		return (1);
    204 
    205 	/*
    206 	 * We really do have two identical symbols for some reason.  Just report
    207 	 * them as equal, and to the lucky one go the spoils.
    208 	 */
    209 	return (0);
    210 }
    211 
    212 /*
    213  * Construct an optimized symbol table sorted by starting address.
    214  */
    215 static void
    216 construct_symtab(dis_tgt_t *tgt)
    217 {
    218 	Elf_Scn *scn;
    219 	GElf_Shdr shdr;
    220 	Elf_Data *symdata;
    221 	int i;
    222 	GElf_Word *symshndx = NULL;
    223 	int symshndx_size;
    224 	sym_entry_t *sym;
    225 	sym_entry_t *p_symtab = NULL;
    226 	int nsym = 0; /* count of symbols we're not interested in */
    227 
    228 	/*
    229 	 * Find the symshndx section, if any
    230 	 */
    231 	for (scn = elf_nextscn(tgt->dt_elf, NULL); scn != NULL;
    232 	    scn = elf_nextscn(tgt->dt_elf, scn)) {
    233 		if (gelf_getshdr(scn, &shdr) == NULL)
    234 			break;
    235 		if (shdr.sh_type == SHT_SYMTAB_SHNDX &&
    236 		    shdr.sh_link == tgt->dt_symidx) {
    237 			Elf_Data	*data;
    238 
    239 			if ((data = elf_getdata(scn, NULL)) != NULL) {
    240 				symshndx = (GElf_Word *)data->d_buf;
    241 				symshndx_size = data->d_size /
    242 				    sizeof (GElf_Word);
    243 				break;
    244 			}
    245 		}
    246 	}
    247 
    248 	if ((scn = elf_getscn(tgt->dt_elf, tgt->dt_symidx)) == NULL)
    249 		die("%s: failed to get section information", tgt->dt_filename);
    250 	if (gelf_getshdr(scn, &shdr) == NULL)
    251 		die("%s: failed to get section header", tgt->dt_filename);
    252 	if (shdr.sh_entsize == 0)
    253 		die("%s: symbol table has zero size", tgt->dt_filename);
    254 
    255 	if ((symdata = elf_getdata(scn, NULL)) == NULL)
    256 		die("%s: failed to get symbol table", tgt->dt_filename);
    257 
    258 	tgt->dt_symcount = symdata->d_size / gelf_fsize(tgt->dt_elf, ELF_T_SYM,
    259 	    1, EV_CURRENT);
    260 
    261 	p_symtab = safe_malloc(tgt->dt_symcount * sizeof (sym_entry_t));
    262 
    263 	for (i = 0, sym = p_symtab; i < tgt->dt_symcount; i++) {
    264 		if (gelf_getsym(symdata, i, &(sym->se_sym)) == NULL) {
    265 			warn("%s: gelf_getsym returned NULL for %d",
    266 			    tgt->dt_filename, i);
    267 			nsym++;
    268 			continue;
    269 		}
    270 
    271 		/*
    272 		 * We're only interested in data symbols.
    273 		 */
    274 		if (!IS_DATA_TYPE(GELF_ST_TYPE(sym->se_sym.st_info))) {
    275 			nsym++;
    276 			continue;
    277 		}
    278 
    279 		if (sym->se_sym.st_shndx == SHN_XINDEX && symshndx != NULL) {
    280 			if (i > symshndx_size) {
    281 				warn("%s: bad SHNX_XINDEX %d",
    282 				    tgt->dt_filename, i);
    283 				sym->se_shndx = -1;
    284 			} else {
    285 				sym->se_shndx = symshndx[i];
    286 			}
    287 		} else {
    288 			sym->se_shndx = sym->se_sym.st_shndx;
    289 		}
    290 
    291 		if ((sym->se_name = elf_strptr(tgt->dt_elf, shdr.sh_link,
    292 		    (size_t)sym->se_sym.st_name)) == NULL) {
    293 			warn("%s: failed to lookup symbol %d name",
    294 			    tgt->dt_filename, i);
    295 			nsym++;
    296 			continue;
    297 		}
    298 
    299 		sym++;
    300 	}
    301 
    302 	tgt->dt_symcount -= nsym;
    303 	tgt->dt_symtab = realloc(p_symtab, tgt->dt_symcount *
    304 	    sizeof (sym_entry_t));
    305 
    306 	qsort(tgt->dt_symtab, tgt->dt_symcount, sizeof (sym_entry_t),
    307 	    sym_compare);
    308 }
    309 
    310 /*
    311  * Create a target backed by an ELF file.
    312  */
    313 dis_tgt_t *
    314 dis_tgt_create(const char *file)
    315 {
    316 	dis_tgt_t *tgt, *current;
    317 	int idx;
    318 	Elf *elf;
    319 	GElf_Ehdr ehdr;
    320 	Elf_Arhdr *arhdr = NULL;
    321 	int cmd;
    322 
    323 	if (elf_version(EV_CURRENT) == EV_NONE)
    324 		die("libelf(3ELF) out of date");
    325 
    326 	tgt = safe_malloc(sizeof (dis_tgt_t));
    327 
    328 	if ((tgt->dt_fd = open(file, O_RDONLY)) < 0) {
    329 		warn("%s: failed opening file, reason: %s", file,
    330 		    strerror(errno));
    331 		free(tgt);
    332 		return (NULL);
    333 	}
    334 
    335 	if ((tgt->dt_elf_root =
    336 	    elf_begin(tgt->dt_fd, ELF_C_READ, NULL)) == NULL) {
    337 		warn("%s: invalid or corrupt ELF file", file);
    338 		dis_tgt_destroy(tgt);
    339 		return (NULL);
    340 	}
    341 
    342 	current = tgt;
    343 	cmd = ELF_C_READ;
    344 	while ((elf = elf_begin(tgt->dt_fd, cmd, tgt->dt_elf_root)) != NULL) {
    345 
    346 		if (elf_kind(tgt->dt_elf_root) == ELF_K_AR &&
    347 		    (arhdr = elf_getarhdr(elf)) == NULL) {
    348 			warn("%s: malformed archive", file);
    349 			dis_tgt_destroy(tgt);
    350 			return (NULL);
    351 		}
    352 
    353 		/*
    354 		 * Make sure that this Elf file is sane
    355 		 */
    356 		if (gelf_getehdr(elf, &ehdr) == NULL) {
    357 			if (arhdr != NULL) {
    358 				/*
    359 				 * For archives, we drive on in the face of bad
    360 				 * members.  The "/" and "//" members are
    361 				 * special, and should be silently ignored.
    362 				 */
    363 				if (strcmp(arhdr->ar_name, "/") != 0 &&
    364 				    strcmp(arhdr->ar_name, "//") != 0)
    365 					warn("%s[%s]: invalid file type",
    366 					    file, arhdr->ar_name);
    367 				cmd = elf_next(elf);
    368 				(void) elf_end(elf);
    369 				continue;
    370 			}
    371 
    372 			warn("%s: invalid file type", file);
    373 			dis_tgt_destroy(tgt);
    374 			return (NULL);
    375 		}
    376 
    377 		/*
    378 		 * If we're seeing a new Elf object, then we have an
    379 		 * archive. In this case, we create a new target, and chain it
    380 		 * off the master target.  We can later iterate over these
    381 		 * targets using dis_tgt_next().
    382 		 */
    383 		if (current->dt_elf != NULL) {
    384 			dis_tgt_t *next = safe_malloc(sizeof (dis_tgt_t));
    385 			next->dt_elf_root = tgt->dt_elf_root;
    386 			next->dt_fd = -1;
    387 			current->dt_next = next;
    388 			current = next;
    389 		}
    390 		current->dt_elf = elf;
    391 		current->dt_arhdr = arhdr;
    392 
    393 		if (elf_getshstrndx(elf, &current->dt_shstrndx) == -1) {
    394 			warn("%s: failed to get section string table for "
    395 			    "file", file);
    396 			dis_tgt_destroy(tgt);
    397 			return (NULL);
    398 		}
    399 
    400 		idx = 0;
    401 		dis_tgt_section_iter(current, get_symtab, &idx);
    402 
    403 		if (current->dt_symidx != 0)
    404 			construct_symtab(current);
    405 
    406 		current->dt_filename = file;
    407 
    408 		cmd = elf_next(elf);
    409 	}
    410 
    411 	/*
    412 	 * Final sanity check.  If we had an archive with no members, then bail
    413 	 * out with a nice message.
    414 	 */
    415 	if (tgt->dt_elf == NULL) {
    416 		warn("%s: empty archive\n", file);
    417 		dis_tgt_destroy(tgt);
    418 		return (NULL);
    419 	}
    420 
    421 	return (tgt);
    422 }
    423 
    424 /*
    425  * Return the filename associated with the target.
    426  */
    427 const char *
    428 dis_tgt_name(dis_tgt_t *tgt)
    429 {
    430 	return (tgt->dt_filename);
    431 }
    432 
    433 /*
    434  * Return the archive member name, if any.
    435  */
    436 const char *
    437 dis_tgt_member(dis_tgt_t *tgt)
    438 {
    439 	if (tgt->dt_arhdr)
    440 		return (tgt->dt_arhdr->ar_name);
    441 	else
    442 		return (NULL);
    443 }
    444 
    445 /*
    446  * Return the Elf_Ehdr associated with this target.  Needed to determine which
    447  * disassembler to use.
    448  */
    449 void
    450 dis_tgt_ehdr(dis_tgt_t *tgt, GElf_Ehdr *ehdr)
    451 {
    452 	(void) gelf_getehdr(tgt->dt_elf, ehdr);
    453 }
    454 
    455 /*
    456  * Return the next target in the list, if this is an archive.
    457  */
    458 dis_tgt_t *
    459 dis_tgt_next(dis_tgt_t *tgt)
    460 {
    461 	return (tgt->dt_next);
    462 }
    463 
    464 /*
    465  * Destroy a target and free up any associated memory.
    466  */
    467 void
    468 dis_tgt_destroy(dis_tgt_t *tgt)
    469 {
    470 	dis_tgt_t *current, *next;
    471 
    472 	current = tgt->dt_next;
    473 	while (current != NULL) {
    474 		next = current->dt_next;
    475 		if (current->dt_elf)
    476 			(void) elf_end(current->dt_elf);
    477 		if (current->dt_symtab)
    478 			free(current->dt_symtab);
    479 		free(current);
    480 		current = next;
    481 	}
    482 
    483 	if (tgt->dt_elf)
    484 		(void) elf_end(tgt->dt_elf);
    485 	if (tgt->dt_elf_root)
    486 		(void) elf_end(tgt->dt_elf_root);
    487 
    488 	if (tgt->dt_symtab)
    489 		free(tgt->dt_symtab);
    490 
    491 	free(tgt);
    492 }
    493 
    494 /*
    495  * Given an address, returns the name of the corresponding symbol, as well as
    496  * the offset within that symbol.  If no matching symbol is found, then NULL is
    497  * returned.
    498  *
    499  * If 'cache_result' is specified, then we keep track of the resulting symbol.
    500  * This cached result is consulted first on subsequent lookups in order to avoid
    501  * unecessary lookups.  This flag should be used for resolving the current PC,
    502  * as the majority of addresses stay within the current function.
    503  */
    504 const char *
    505 dis_tgt_lookup(dis_tgt_t *tgt, uint64_t addr, off_t *offset, int cache_result,
    506     size_t *size, int *isfunc)
    507 {
    508 	int lo, hi, mid;
    509 	sym_entry_t *sym, *osym, *match;
    510 	int found;
    511 
    512 	if (tgt->dt_symcache != NULL &&
    513 	    addr >= tgt->dt_symcache->se_sym.st_value &&
    514 	    addr < tgt->dt_symcache->se_sym.st_value +
    515 	    tgt->dt_symcache->se_sym.st_size) {
    516 		*offset = addr - tgt->dt_symcache->se_sym.st_value;
    517 		*size = tgt->dt_symcache->se_sym.st_size;
    518 		return (tgt->dt_symcache->se_name);
    519 	}
    520 
    521 	lo = 0;
    522 	hi = (tgt->dt_symcount - 1);
    523 	found = 0;
    524 	match = osym = NULL;
    525 	while (lo <= hi) {
    526 		mid = (lo + hi) / 2;
    527 
    528 		sym = &tgt->dt_symtab[mid];
    529 
    530 		if (addr >= sym->se_sym.st_value &&
    531 		    addr < sym->se_sym.st_value + sym->se_sym.st_size &&
    532 		    (!found || sym->se_sym.st_value > osym->se_sym.st_value)) {
    533 			osym = sym;
    534 			found = 1;
    535 		} else if (addr == sym->se_sym.st_value) {
    536 			/*
    537 			 * Particularly for .plt objects, it's possible to have
    538 			 * a zero sized object.  We want to return this, but we
    539 			 * want it to be a last resort.
    540 			 */
    541 			match = sym;
    542 		}
    543 
    544 		if (addr < sym->se_sym.st_value)
    545 			hi = mid - 1;
    546 		else
    547 			lo = mid + 1;
    548 	}
    549 
    550 	if (!found) {
    551 		if (match)
    552 			osym = match;
    553 		else
    554 			return (NULL);
    555 	}
    556 
    557 	/*
    558 	 * Walk backwards to find the best match.
    559 	 */
    560 	do {
    561 		sym = osym;
    562 
    563 		if (osym == tgt->dt_symtab)
    564 			break;
    565 
    566 		osym = osym - 1;
    567 	} while ((sym->se_sym.st_value == osym->se_sym.st_value) &&
    568 	    (addr >= osym->se_sym.st_value) &&
    569 	    (addr < osym->se_sym.st_value + osym->se_sym.st_size));
    570 
    571 	if (cache_result)
    572 		tgt->dt_symcache = sym;
    573 
    574 	*offset = addr - sym->se_sym.st_value;
    575 	*size = sym->se_sym.st_size;
    576 	if (isfunc)
    577 		*isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) == STT_FUNC);
    578 
    579 	return (sym->se_name);
    580 }
    581 
    582 /*
    583  * Given an address, return the starting offset of the next symbol in the file.
    584  * Relies on the fact that this is only used when we encounter a bad instruction
    585  * in the input stream, so we know that the last symbol looked up will be in the
    586  * cache.
    587  */
    588 off_t
    589 dis_tgt_next_symbol(dis_tgt_t *tgt, uint64_t addr)
    590 {
    591 	sym_entry_t *sym = tgt->dt_symcache;
    592 	uint64_t start;
    593 
    594 	/* make sure the cached symbol and address are valid */
    595 	if (sym == NULL || addr < sym->se_sym.st_value ||
    596 	    addr >= sym->se_sym.st_value + sym->se_sym.st_size)
    597 		return (0);
    598 
    599 	start = sym->se_sym.st_value;
    600 
    601 	/* find the next symbol */
    602 	while (sym != tgt->dt_symtab + tgt->dt_symcount &&
    603 	    sym->se_sym.st_value == start)
    604 		sym++;
    605 
    606 	return (sym->se_sym.st_value - addr);
    607 }
    608 
    609 /*
    610  * Iterate over all sections in the target, executing the given callback for
    611  * each.
    612  */
    613 void
    614 dis_tgt_section_iter(dis_tgt_t *tgt, section_iter_f func, void *data)
    615 {
    616 	dis_scn_t sdata;
    617 	Elf_Scn *scn;
    618 	int idx;
    619 
    620 	for (scn = elf_nextscn(tgt->dt_elf, NULL), idx = 1; scn != NULL;
    621 	    scn = elf_nextscn(tgt->dt_elf, scn), idx++) {
    622 
    623 		if (gelf_getshdr(scn, &sdata.ds_shdr) == NULL) {
    624 			warn("%s: failed to get section %d header",
    625 			    tgt->dt_filename, idx);
    626 			continue;
    627 		}
    628 
    629 		if ((sdata.ds_name = elf_strptr(tgt->dt_elf, tgt->dt_shstrndx,
    630 		    sdata.ds_shdr.sh_name)) == NULL) {
    631 			warn("%s: failed to get section %d name",
    632 			    tgt->dt_filename, idx);
    633 			continue;
    634 		}
    635 
    636 		if ((sdata.ds_data = elf_getdata(scn, NULL)) == NULL) {
    637 			warn("%s: failed to get data for section '%s'",
    638 			    tgt->dt_filename, sdata.ds_name);
    639 			continue;
    640 		}
    641 
    642 		func(tgt, &sdata, data);
    643 	}
    644 }
    645 
    646 /*
    647  * Return 1 if the given section contains text, 0 otherwise.
    648  */
    649 int
    650 dis_section_istext(dis_scn_t *scn)
    651 {
    652 	return ((scn->ds_shdr.sh_type == SHT_PROGBITS) &&
    653 	    (scn->ds_shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR)));
    654 }
    655 
    656 /*
    657  * Return a pointer to the section data.
    658  */
    659 void *
    660 dis_section_data(dis_scn_t *scn)
    661 {
    662 	return (scn->ds_data->d_buf);
    663 }
    664 
    665 /*
    666  * Return the size of the section data.
    667  */
    668 size_t
    669 dis_section_size(dis_scn_t *scn)
    670 {
    671 	return (scn->ds_data->d_size);
    672 }
    673 
    674 /*
    675  * Return the address for the given section.
    676  */
    677 uint64_t
    678 dis_section_addr(dis_scn_t *scn)
    679 {
    680 	return (scn->ds_shdr.sh_addr);
    681 }
    682 
    683 /*
    684  * Return the name of the current section.
    685  */
    686 const char *
    687 dis_section_name(dis_scn_t *scn)
    688 {
    689 	return (scn->ds_name);
    690 }
    691 
    692 /*
    693  * Create an allocated copy of the given section
    694  */
    695 dis_scn_t *
    696 dis_section_copy(dis_scn_t *scn)
    697 {
    698 	dis_scn_t *new;
    699 
    700 	new = safe_malloc(sizeof (dis_scn_t));
    701 	(void) memcpy(new, scn, sizeof (dis_scn_t));
    702 
    703 	return (new);
    704 }
    705 
    706 /*
    707  * Free section memory
    708  */
    709 void
    710 dis_section_free(dis_scn_t *scn)
    711 {
    712 	free(scn);
    713 }
    714 
    715 /*
    716  * Iterate over all functions in the target, executing the given callback for
    717  * each one.
    718  */
    719 void
    720 dis_tgt_function_iter(dis_tgt_t *tgt, function_iter_f func, void *data)
    721 {
    722 	int i;
    723 	sym_entry_t *sym;
    724 	dis_func_t df;
    725 	Elf_Scn *scn;
    726 	GElf_Shdr	shdr;
    727 
    728 	for (i = 0, sym = tgt->dt_symtab; i < tgt->dt_symcount; i++, sym++) {
    729 
    730 		/* ignore non-functions */
    731 		if ((GELF_ST_TYPE(sym->se_sym.st_info) != STT_FUNC) ||
    732 		    (sym->se_name == NULL) ||
    733 		    (sym->se_sym.st_size == 0) ||
    734 		    (sym->se_shndx >= SHN_LORESERVE))
    735 			continue;
    736 
    737 		/* get the ELF data associated with this function */
    738 		if ((scn = elf_getscn(tgt->dt_elf, sym->se_shndx)) == NULL ||
    739 		    gelf_getshdr(scn, &shdr) == NULL ||
    740 		    (df.df_data = elf_getdata(scn, NULL)) == NULL ||
    741 		    df.df_data->d_size == 0) {
    742 			warn("%s: failed to read section %d",
    743 			    tgt->dt_filename, sym->se_shndx);
    744 			continue;
    745 		}
    746 
    747 		/*
    748 		 * Verify that the address lies within the section that we think
    749 		 * it does.
    750 		 */
    751 		if (sym->se_sym.st_value < shdr.sh_addr ||
    752 		    (sym->se_sym.st_value + sym->se_sym.st_size) >
    753 		    (shdr.sh_addr + shdr.sh_size)) {
    754 			warn("%s: bad section %d for address %p",
    755 			    tgt->dt_filename, sym->se_sym.st_shndx,
    756 			    sym->se_sym.st_value);
    757 			continue;
    758 		}
    759 
    760 		df.df_sym = sym;
    761 		df.df_offset = sym->se_sym.st_value - shdr.sh_addr;
    762 
    763 		func(tgt, &df, data);
    764 	}
    765 }
    766 
    767 /*
    768  * Return the data associated with a given function.
    769  */
    770 void *
    771 dis_function_data(dis_func_t *func)
    772 {
    773 	return ((char *)func->df_data->d_buf + func->df_offset);
    774 }
    775 
    776 /*
    777  * Return the size of a function.
    778  */
    779 size_t
    780 dis_function_size(dis_func_t *func)
    781 {
    782 	return (func->df_sym->se_sym.st_size);
    783 }
    784 
    785 /*
    786  * Return the address of a function.
    787  */
    788 uint64_t
    789 dis_function_addr(dis_func_t *func)
    790 {
    791 	return (func->df_sym->se_sym.st_value);
    792 }
    793 
    794 /*
    795  * Return the name of the function
    796  */
    797 const char *
    798 dis_function_name(dis_func_t *func)
    799 {
    800 	return (func->df_sym->se_name);
    801 }
    802 
    803 /*
    804  * Return a copy of a function.
    805  */
    806 dis_func_t *
    807 dis_function_copy(dis_func_t *func)
    808 {
    809 	dis_func_t *new;
    810 
    811 	new = safe_malloc(sizeof (dis_func_t));
    812 	(void) memcpy(new, func, sizeof (dis_func_t));
    813 
    814 	return (new);
    815 }
    816 
    817 /*
    818  * Free function memory
    819  */
    820 void
    821 dis_function_free(dis_func_t *func)
    822 {
    823 	free(func);
    824 }
    825