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