dis_target.c revision 62b628a68db596a2d75a316dc7ffef658079231f
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 */
55typedef 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 */
67struct 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 */
86struct 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 */
97struct 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 */
114static void
115get_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
130static int
131sym_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 */
213static void
214construct_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 */
311dis_tgt_t *
312dis_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 */
425const char *
426dis_tgt_name(dis_tgt_t *tgt)
427{
428	return (tgt->dt_filename);
429}
430
431/*
432 * Return the archive member name, if any.
433 */
434const char *
435dis_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 */
447void
448dis_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 */
456dis_tgt_t *
457dis_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 */
465void
466dis_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 */
502const char *
503dis_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 */
586off_t
587dis_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 */
611void
612dis_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 */
647int
648dis_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 */
657void *
658dis_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 */
666size_t
667dis_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 */
675uint64_t
676dis_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 */
684const char *
685dis_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 */
693dis_scn_t *
694dis_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 */
707void
708dis_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 */
717void
718dis_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 */
768void *
769dis_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 */
777size_t
778dis_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 */
786uint64_t
787dis_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 */
795const char *
796dis_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 */
804dis_func_t *
805dis_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 */
818void
819dis_function_free(dis_func_t *func)
820{
821	free(func);
822}
823