dis_target.c revision 23a1ccea6aac035f084a7a4cdc968687d1b02daf
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 */
54typedef 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 */
66struct 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 */
85struct 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 */
96struct 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 */
112static void
113get_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
128static int
129sym_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 */
211static void
212construct_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 */
309dis_tgt_t *
310dis_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 */
423const char *
424dis_tgt_name(dis_tgt_t *tgt)
425{
426	return (tgt->dt_filename);
427}
428
429/*
430 * Return the archive member name, if any.
431 */
432const char *
433dis_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 */
445void
446dis_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 */
454dis_tgt_t *
455dis_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 */
463void
464dis_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 */
500const char *
501dis_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 */
584off_t
585dis_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 */
609void
610dis_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 */
645int
646dis_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 */
655void *
656dis_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 */
664size_t
665dis_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 */
673uint64_t
674dis_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 */
682const char *
683dis_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 */
691dis_scn_t *
692dis_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 */
705void
706dis_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 */
715void
716dis_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 */
766void *
767dis_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 */
775size_t
776dis_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 */
784uint64_t
785dis_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 */
793const char *
794dis_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 */
802dis_func_t *
803dis_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 */
816void
817dis_function_free(dis_func_t *func)
818{
819	free(func);
820}
821