xref: /illumos-gate/usr/src/cmd/dis/dis_target.c (revision 0173c38a73f34277e0c97a19fedfd25d81ba8380)
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 2006 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 		(void) memset(sym, sizeof (sym_entry_t), 0);
265 		if (gelf_getsym(symdata, i, &(sym->se_sym)) == NULL) {
266 			warn("%s: gelf_getsym returned NULL for %d",
267 				tgt->dt_filename, i);
268 			nsym++;
269 			continue;
270 		}
271 
272 		/*
273 		 * We're only interested in data symbols.
274 		 */
275 		if (!IS_DATA_TYPE(GELF_ST_TYPE(sym->se_sym.st_info))) {
276 			nsym++;
277 			continue;
278 		}
279 
280 		if (sym->se_sym.st_shndx == SHN_XINDEX && symshndx != NULL) {
281 			if (i > symshndx_size) {
282 				warn("%s: bad SHNX_XINDEX %d",
283 					tgt->dt_filename, i);
284 				sym->se_shndx = -1;
285 			} else {
286 				sym->se_shndx = symshndx[i];
287 			}
288 		} else {
289 			sym->se_shndx = sym->se_sym.st_shndx;
290 		}
291 
292 		if ((sym->se_name = elf_strptr(tgt->dt_elf, shdr.sh_link,
293 		    (size_t)sym->se_sym.st_name)) == NULL) {
294 			warn("%s: failed to lookup symbol %d name",
295 				tgt->dt_filename, i);
296 			nsym++;
297 			continue;
298 		}
299 
300 		sym++;
301 	}
302 
303 	tgt->dt_symcount -= nsym;
304 	tgt->dt_symtab = realloc(p_symtab,
305 				tgt->dt_symcount * sizeof (sym_entry_t));
306 
307 	qsort(tgt->dt_symtab, tgt->dt_symcount, sizeof (sym_entry_t),
308 	    sym_compare);
309 }
310 
311 /*
312  * Create a target backed by an ELF file.
313  */
314 dis_tgt_t *
315 dis_tgt_create(const char *file)
316 {
317 	dis_tgt_t *tgt, *current;
318 	int idx;
319 	Elf *elf;
320 	GElf_Ehdr ehdr;
321 	Elf_Arhdr *arhdr = NULL;
322 	int cmd;
323 
324 	if (elf_version(EV_CURRENT) == EV_NONE)
325 		die("libelf(3ELF) out of date");
326 
327 	tgt = safe_malloc(sizeof (dis_tgt_t));
328 
329 	if ((tgt->dt_fd = open(file, O_RDONLY)) < 0) {
330 		warn("%s: failed opening file, reason: %s", file,
331 			strerror(errno));
332 		free(tgt);
333 		return (NULL);
334 	}
335 
336 	if ((tgt->dt_elf_root =
337 	    elf_begin(tgt->dt_fd, ELF_C_READ, NULL)) == NULL) {
338 		warn("%s: invalid or corrupt ELF file", file);
339 		dis_tgt_destroy(tgt);
340 		return (NULL);
341 	}
342 
343 	current = tgt;
344 	cmd = ELF_C_READ;
345 	while ((elf = elf_begin(tgt->dt_fd, cmd, tgt->dt_elf_root)) != NULL) {
346 
347 		if (elf_kind(tgt->dt_elf_root) == ELF_K_AR &&
348 		    (arhdr = elf_getarhdr(elf)) == NULL) {
349 			warn("%s: malformed archive", file);
350 			dis_tgt_destroy(tgt);
351 			return (NULL);
352 		}
353 
354 		/*
355 		 * Make sure that this Elf file is sane
356 		 */
357 		if (gelf_getehdr(elf, &ehdr) == NULL) {
358 			if (arhdr != NULL) {
359 				/*
360 				 * For archives, we drive on in the face of bad
361 				 * members.  The "/" and "//" members are
362 				 * special, and should be silently ignored.
363 				 */
364 				if (strcmp(arhdr->ar_name, "/") != 0 &&
365 				    strcmp(arhdr->ar_name, "//") != 0)
366 					warn("%s[%s]: invalid file type",
367 					    file, arhdr->ar_name);
368 				cmd = elf_next(elf);
369 				(void) elf_end(elf);
370 				continue;
371 			}
372 
373 			warn("%s: invalid file type", file);
374 			dis_tgt_destroy(tgt);
375 			return (NULL);
376 		}
377 
378 		/*
379 		 * If we're seeing a new Elf object, then we have an
380 		 * archive. In this case, we create a new target, and chain it
381 		 * off the master target.  We can later iterate over these
382 		 * targets using dis_tgt_next().
383 		 */
384 		if (current->dt_elf != NULL) {
385 			dis_tgt_t *next = safe_malloc(sizeof (dis_tgt_t));
386 			next->dt_elf_root = tgt->dt_elf_root;
387 			next->dt_fd = -1;
388 			current->dt_next = next;
389 			current = next;
390 		}
391 		current->dt_elf = elf;
392 		current->dt_arhdr = arhdr;
393 
394 		if (elf_getshstrndx(elf, &current->dt_shstrndx) == -1) {
395 			warn("%s: failed to get section string table for "
396 			    "file", file);
397 			dis_tgt_destroy(tgt);
398 			return (NULL);
399 		}
400 
401 		idx = 0;
402 		dis_tgt_section_iter(current, get_symtab, &idx);
403 
404 		if (current->dt_symidx != 0)
405 			construct_symtab(current);
406 
407 		current->dt_filename = file;
408 
409 		cmd = elf_next(elf);
410 	}
411 
412 	/*
413 	 * Final sanity check.  If we had an archive with no members, then bail
414 	 * out with a nice message.
415 	 */
416 	if (tgt->dt_elf == NULL) {
417 		warn("%s: empty archive\n", file);
418 		dis_tgt_destroy(tgt);
419 		return (NULL);
420 	}
421 
422 	return (tgt);
423 }
424 
425 /*
426  * Return the filename associated with the target.
427  */
428 const char *
429 dis_tgt_name(dis_tgt_t *tgt)
430 {
431 	return (tgt->dt_filename);
432 }
433 
434 /*
435  * Return the archive member name, if any.
436  */
437 const char *
438 dis_tgt_member(dis_tgt_t *tgt)
439 {
440 	if (tgt->dt_arhdr)
441 		return (tgt->dt_arhdr->ar_name);
442 	else
443 		return (NULL);
444 }
445 
446 /*
447  * Return the Elf_Ehdr associated with this target.  Needed to determine which
448  * disassembler to use.
449  */
450 void
451 dis_tgt_ehdr(dis_tgt_t *tgt, GElf_Ehdr *ehdr)
452 {
453 	(void) gelf_getehdr(tgt->dt_elf, ehdr);
454 }
455 
456 /*
457  * Return the next target in the list, if this is an archive.
458  */
459 dis_tgt_t *
460 dis_tgt_next(dis_tgt_t *tgt)
461 {
462 	return (tgt->dt_next);
463 }
464 
465 /*
466  * Destroy a target and free up any associated memory.
467  */
468 void
469 dis_tgt_destroy(dis_tgt_t *tgt)
470 {
471 	dis_tgt_t *current, *next;
472 
473 	current = tgt->dt_next;
474 	while (current != NULL) {
475 		next = current->dt_next;
476 		if (current->dt_elf)
477 			(void) elf_end(current->dt_elf);
478 		if (current->dt_symtab)
479 			free(current->dt_symtab);
480 		free(current);
481 		current = next;
482 	}
483 
484 	if (tgt->dt_elf)
485 		(void) elf_end(tgt->dt_elf);
486 	if (tgt->dt_elf_root)
487 		(void) elf_end(tgt->dt_elf_root);
488 
489 	if (tgt->dt_symtab)
490 		free(tgt->dt_symtab);
491 
492 	free(tgt);
493 }
494 
495 /*
496  * Given an address, returns the name of the corresponding symbol, as well as
497  * the offset within that symbol.  If no matching symbol is found, then NULL is
498  * returned.
499  *
500  * If 'cache_result' is specified, then we keep track of the resulting symbol.
501  * This cached result is consulted first on subsequent lookups in order to avoid
502  * unecessary lookups.  This flag should be used for resolving the current PC,
503  * as the majority of addresses stay within the current function.
504  */
505 const char *
506 dis_tgt_lookup(dis_tgt_t *tgt, uint64_t addr, off_t *offset, int cache_result,
507     size_t *size, int *isfunc)
508 {
509 	int lo, hi, mid;
510 	sym_entry_t *sym, *osym, *match;
511 	int found;
512 
513 	if (tgt->dt_symcache != NULL &&
514 	    addr >= tgt->dt_symcache->se_sym.st_value &&
515 	    addr < tgt->dt_symcache->se_sym.st_value +
516 	    tgt->dt_symcache->se_sym.st_size) {
517 		*offset = addr - tgt->dt_symcache->se_sym.st_value;
518 		*size = tgt->dt_symcache->se_sym.st_size;
519 		return (tgt->dt_symcache->se_name);
520 	}
521 
522 	lo = 0;
523 	hi = (tgt->dt_symcount - 1);
524 	found = 0;
525 	match = osym = NULL;
526 	while (lo <= hi) {
527 		mid = (lo + hi) / 2;
528 
529 		sym = &tgt->dt_symtab[mid];
530 
531 		if (addr >= sym->se_sym.st_value &&
532 		    addr < sym->se_sym.st_value + sym->se_sym.st_size &&
533 		    (!found || sym->se_sym.st_value > osym->se_sym.st_value)) {
534 			osym = sym;
535 			found = 1;
536 		} else if (addr == sym->se_sym.st_value) {
537 			/*
538 			 * Particularly for .plt objects, it's possible to have
539 			 * a zero sized object.  We want to return this, but we
540 			 * want it to be a last resort.
541 			 */
542 			match = sym;
543 		}
544 
545 		if (addr < sym->se_sym.st_value)
546 			hi = mid - 1;
547 		else
548 			lo = mid + 1;
549 	}
550 
551 	if (!found) {
552 		if (match)
553 			osym = match;
554 		else
555 			return (NULL);
556 	}
557 
558 	/*
559 	 * Walk backwards to find the best match.
560 	 */
561 	do {
562 		sym = osym;
563 
564 		if (osym == tgt->dt_symtab)
565 			break;
566 
567 		osym = osym - 1;
568 	} while ((sym->se_sym.st_value == osym->se_sym.st_value) &&
569 		(addr >= osym->se_sym.st_value) &&
570 		(addr < osym->se_sym.st_value + osym->se_sym.st_size));
571 
572 	if (cache_result)
573 		tgt->dt_symcache = sym;
574 
575 	*offset = addr - sym->se_sym.st_value;
576 	*size = sym->se_sym.st_size;
577 	if (isfunc)
578 		*isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) == STT_FUNC);
579 
580 	return (sym->se_name);
581 }
582 
583 /*
584  * Given an address, return the starting offset of the next symbol in the file.
585  * Relies on the fact that this is only used when we encounter a bad instruction
586  * in the input stream, so we know that the last symbol looked up will be in the
587  * cache.
588  */
589 off_t
590 dis_tgt_next_symbol(dis_tgt_t *tgt, uint64_t addr)
591 {
592 	sym_entry_t *sym = tgt->dt_symcache;
593 	uint64_t start;
594 
595 	/* make sure the cached symbol and address are valid */
596 	if (sym == NULL || addr < sym->se_sym.st_value ||
597 	    addr >= sym->se_sym.st_value + sym->se_sym.st_size)
598 		return (0);
599 
600 	start = sym->se_sym.st_value;
601 
602 	/* find the next symbol */
603 	while (sym != tgt->dt_symtab + tgt->dt_symcount &&
604 	    sym->se_sym.st_value == start)
605 		sym++;
606 
607 	return (sym->se_sym.st_value - addr);
608 }
609 
610 /*
611  * Iterate over all sections in the target, executing the given callback for
612  * each.
613  */
614 void
615 dis_tgt_section_iter(dis_tgt_t *tgt, section_iter_f func, void *data)
616 {
617 	dis_scn_t sdata;
618 	Elf_Scn *scn;
619 	int idx;
620 
621 	for (scn = elf_nextscn(tgt->dt_elf, NULL), idx = 1; scn != NULL;
622 	    scn = elf_nextscn(tgt->dt_elf, scn), idx++) {
623 
624 		if (gelf_getshdr(scn, &sdata.ds_shdr) == NULL) {
625 			warn("%s: failed to get section %d header",
626 			    tgt->dt_filename, idx);
627 			continue;
628 		}
629 
630 		if ((sdata.ds_name = elf_strptr(tgt->dt_elf, tgt->dt_shstrndx,
631 		    sdata.ds_shdr.sh_name)) == NULL) {
632 			warn("%s: failed to get section %d name",
633 			    tgt->dt_filename, idx);
634 			continue;
635 		}
636 
637 		if ((sdata.ds_data = elf_getdata(scn, NULL)) == NULL) {
638 			warn("%s: failed to get data for section '%s'",
639 			    tgt->dt_filename, sdata.ds_name);
640 			continue;
641 		}
642 
643 		func(tgt, &sdata, data);
644 	}
645 }
646 
647 /*
648  * Return 1 if the given section contains text, 0 otherwise.
649  */
650 int
651 dis_section_istext(dis_scn_t *scn)
652 {
653 	return ((scn->ds_shdr.sh_type == SHT_PROGBITS) &&
654 	    (scn->ds_shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR)));
655 }
656 
657 /*
658  * Return a pointer to the section data.
659  */
660 void *
661 dis_section_data(dis_scn_t *scn)
662 {
663 	return (scn->ds_data->d_buf);
664 }
665 
666 /*
667  * Return the size of the section data.
668  */
669 size_t
670 dis_section_size(dis_scn_t *scn)
671 {
672 	return (scn->ds_data->d_size);
673 }
674 
675 /*
676  * Return the address for the given section.
677  */
678 uint64_t
679 dis_section_addr(dis_scn_t *scn)
680 {
681 	return (scn->ds_shdr.sh_addr);
682 }
683 
684 /*
685  * Return the name of the current section.
686  */
687 const char *
688 dis_section_name(dis_scn_t *scn)
689 {
690 	return (scn->ds_name);
691 }
692 
693 /*
694  * Create an allocated copy of the given section
695  */
696 dis_scn_t *
697 dis_section_copy(dis_scn_t *scn)
698 {
699 	dis_scn_t *new;
700 
701 	new = safe_malloc(sizeof (dis_scn_t));
702 	(void) memcpy(new, scn, sizeof (dis_scn_t));
703 
704 	return (new);
705 }
706 
707 /*
708  * Free section memory
709  */
710 void
711 dis_section_free(dis_scn_t *scn)
712 {
713 	free(scn);
714 }
715 
716 /*
717  * Iterate over all functions in the target, executing the given callback for
718  * each one.
719  */
720 void
721 dis_tgt_function_iter(dis_tgt_t *tgt, function_iter_f func, void *data)
722 {
723 	int i;
724 	sym_entry_t *sym;
725 	dis_func_t df;
726 	Elf_Scn *scn;
727 	GElf_Shdr	shdr;
728 
729 	for (i = 0, sym = tgt->dt_symtab; i < tgt->dt_symcount; i++, sym++) {
730 
731 		/* ignore non-functions */
732 		if ((GELF_ST_TYPE(sym->se_sym.st_info) != STT_FUNC) ||
733 		    (sym->se_name == NULL) ||
734 		    (sym->se_sym.st_size == 0) ||
735 		    (sym->se_shndx >= SHN_LORESERVE))
736 			continue;
737 
738 		/* get the ELF data associated with this function */
739 		if ((scn = elf_getscn(tgt->dt_elf, sym->se_shndx)) == NULL ||
740 		    gelf_getshdr(scn, &shdr) == NULL ||
741 		    (df.df_data = elf_getdata(scn, NULL)) == NULL ||
742 		    df.df_data->d_size == 0) {
743 			warn("%s: failed to read section %d",
744 			    tgt->dt_filename, sym->se_shndx);
745 			continue;
746 		}
747 
748 		/*
749 		 * Verify that the address lies within the section that we think
750 		 * it does.
751 		 */
752 		if (sym->se_sym.st_value < shdr.sh_addr ||
753 		    (sym->se_sym.st_value + sym->se_sym.st_size) >
754 		    (shdr.sh_addr + shdr.sh_size)) {
755 			warn("%s: bad section %d for address %p",
756 				tgt->dt_filename, sym->se_sym.st_shndx,
757 				sym->se_sym.st_value);
758 			continue;
759 		}
760 
761 		df.df_sym = sym;
762 		df.df_offset = sym->se_sym.st_value - shdr.sh_addr;
763 
764 		func(tgt, &df, data);
765 	}
766 }
767 
768 /*
769  * Return the data associated with a given function.
770  */
771 void *
772 dis_function_data(dis_func_t *func)
773 {
774 	return ((char *)func->df_data->d_buf + func->df_offset);
775 }
776 
777 /*
778  * Return the size of a function.
779  */
780 size_t
781 dis_function_size(dis_func_t *func)
782 {
783 	return (func->df_sym->se_sym.st_size);
784 }
785 
786 /*
787  * Return the address of a function.
788  */
789 uint64_t
790 dis_function_addr(dis_func_t *func)
791 {
792 	return (func->df_sym->se_sym.st_value);
793 }
794 
795 /*
796  * Return the name of the function
797  */
798 const char *
799 dis_function_name(dis_func_t *func)
800 {
801 	return (func->df_sym->se_name);
802 }
803 
804 /*
805  * Return a copy of a function.
806  */
807 dis_func_t *
808 dis_function_copy(dis_func_t *func)
809 {
810 	dis_func_t *new;
811 
812 	new = safe_malloc(sizeof (dis_func_t));
813 	(void) memcpy(new, func, sizeof (dis_func_t));
814 
815 	return (new);
816 }
817 
818 /*
819  * Free function memory
820  */
821 void
822 dis_function_free(dis_func_t *func)
823 {
824 	free(func);
825 }
826