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