dis_target.c revision 161d9479d65efe70fd8e3fb443027646ce1a5990
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 */
57typedef 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 */
69struct 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 */
88struct 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 */
99struct 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 */
116static void
117get_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
132static int
133sym_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 */
215static void
216construct_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 */
314dis_tgt_t *
315dis_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 */
428const char *
429dis_tgt_name(dis_tgt_t *tgt)
430{
431	return (tgt->dt_filename);
432}
433
434/*
435 * Return the archive member name, if any.
436 */
437const char *
438dis_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 */
450void
451dis_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 */
459dis_tgt_t *
460dis_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 */
468void
469dis_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 */
505const char *
506dis_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 */
589off_t
590dis_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 */
614void
615dis_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 */
650int
651dis_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 */
660void *
661dis_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 */
669size_t
670dis_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 */
678uint64_t
679dis_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 */
687const char *
688dis_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 */
696dis_scn_t *
697dis_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 */
710void
711dis_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 */
720void
721dis_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 */
771void *
772dis_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 */
780size_t
781dis_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 */
789uint64_t
790dis_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 */
798const char *
799dis_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 */
807dis_func_t *
808dis_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 */
821void
822dis_function_free(dis_func_t *func)
823{
824	free(func);
825}
826