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.
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.
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]
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
25 * Copyright 2011 Jason King. All rights reserved.
37 #include <sys/fcntl.h>
39 #include <sys/sysmacros.h>
40 #include <sys/types.h>
42 #include "dis_target.h"
46 * Standard ELF disassembler target.
48 * We only support disassembly of ELF files, though this target interface could
49 * be extended in the future. Each basic type (target, func, section) contains
50 * enough information to uniquely identify the location within the file. The
51 * interfaces use libelf(3LIB) to do the actual processing of the file.
55 * Symbol table entry type. We maintain our own symbol table sorted by address,
56 * with the symbol name already resolved against the ELF symbol table.
58 typedef struct sym_entry
{
59 GElf_Sym se_sym
; /* value of symbol */
60 char *se_name
; /* name of symbol */
61 int se_shndx
; /* section where symbol is located */
65 * Create a map of the virtual address ranges of every section. This will
66 * allow us to create dummpy mappings for unassigned addresses. Otherwise
67 * multiple sections with unassigned addresses will appear to overlap and
68 * mess up symbol resolution (which uses the virtual address).
70 typedef struct dis_shnmap
{
71 const char *dm_name
; /* name of section */
72 uint64_t dm_start
; /* virtual address of section */
73 size_t dm_length
; /* address length */
74 boolean_t dm_mapped
; /* did we assign the mapping */
78 * Target data structure. This structure keeps track of the ELF file
79 * information, a few bits of pre-processed section index information, and
80 * sorted versions of the symbol table. We also keep track of the last symbol
81 * looked up, as the majority of lookups remain within the same symbol.
84 Elf
*dt_elf
; /* libelf handle */
85 Elf
*dt_elf_root
; /* main libelf handle (for archives) */
86 const char *dt_filename
; /* name of file */
87 int dt_fd
; /* underlying file descriptor */
88 size_t dt_shstrndx
; /* section index of .shstrtab */
89 size_t dt_symidx
; /* section index of symbol table */
90 sym_entry_t
*dt_symcache
; /* last symbol looked up */
91 sym_entry_t
*dt_symtab
; /* sorted symbol table */
92 int dt_symcount
; /* # of symbol table entries */
93 struct dis_tgt
*dt_next
; /* next target (for archives) */
94 Elf_Arhdr
*dt_arhdr
; /* archive header (for archives) */
95 dis_shnmap_t
*dt_shnmap
; /* section address map */
96 size_t dt_shncount
; /* # of sections in target */
100 * Function data structure. We resolve the symbol and lookup the associated ELF
101 * data when building this structure. The offset is calculated based on the
102 * section's starting address.
105 sym_entry_t
*df_sym
; /* symbol table reference */
106 Elf_Data
*df_data
; /* associated ELF data */
107 size_t df_offset
; /* offset within data */
111 * Section data structure. We store the entire section header so that we can
112 * determine some properties (such as whether or not it contains text) after
113 * building the structure.
121 /* Lifted from Psymtab.c, omitting STT_TLS */
123 ((1 << STT_OBJECT) | (1 << STT_FUNC) | (1 << STT_COMMON))
124 #define IS_DATA_TYPE(tp) (((1 << (tp)) & DATA_TYPES) != 0)
127 * Save the virtual address range for this section and select the
128 * best section to use as the symbol table. We prefer SHT_SYMTAB
133 tgt_scn_init(dis_tgt_t
*tgt
, dis_scn_t
*scn
, void *data
)
139 tgt
->dt_shnmap
[*index
].dm_name
= scn
->ds_name
;
140 tgt
->dt_shnmap
[*index
].dm_start
= scn
->ds_shdr
.sh_addr
;
141 tgt
->dt_shnmap
[*index
].dm_length
= scn
->ds_shdr
.sh_size
;
142 tgt
->dt_shnmap
[*index
].dm_mapped
= B_FALSE
;
145 * Prefer SHT_SYMTAB over SHT_DYNSYM
147 if (scn
->ds_shdr
.sh_type
== SHT_DYNSYM
&& tgt
->dt_symidx
== 0)
148 tgt
->dt_symidx
= *index
;
149 else if (scn
->ds_shdr
.sh_type
== SHT_SYMTAB
)
150 tgt
->dt_symidx
= *index
;
154 sym_compare(const void *a
, const void *b
)
156 const sym_entry_t
*syma
= a
;
157 const sym_entry_t
*symb
= b
;
158 const char *aname
= syma
->se_name
;
159 const char *bname
= symb
->se_name
;
161 if (syma
->se_sym
.st_value
< symb
->se_sym
.st_value
)
164 if (syma
->se_sym
.st_value
> symb
->se_sym
.st_value
)
168 * Prefer functions over non-functions
170 if (GELF_ST_TYPE(syma
->se_sym
.st_info
) !=
171 GELF_ST_TYPE(symb
->se_sym
.st_info
)) {
172 if (GELF_ST_TYPE(syma
->se_sym
.st_info
) == STT_FUNC
)
174 if (GELF_ST_TYPE(symb
->se_sym
.st_info
) == STT_FUNC
)
179 * For symbols with the same address and type, we sort them according to
182 * 1. weak symbols (common name)
183 * 2. global symbols (external name)
186 if (GELF_ST_BIND(syma
->se_sym
.st_info
) !=
187 GELF_ST_BIND(symb
->se_sym
.st_info
)) {
188 if (GELF_ST_BIND(syma
->se_sym
.st_info
) == STB_WEAK
)
190 if (GELF_ST_BIND(symb
->se_sym
.st_info
) == STB_WEAK
)
193 if (GELF_ST_BIND(syma
->se_sym
.st_info
) == STB_GLOBAL
)
195 if (GELF_ST_BIND(symb
->se_sym
.st_info
) == STB_GLOBAL
)
200 * As a last resort, if we have multiple symbols of the same type at the
201 * same address, prefer the version with the fewest leading underscores.
208 while (*aname
== '_' && *bname
== '_') {
219 * Prefer the symbol with the smaller size.
221 if (syma
->se_sym
.st_size
< symb
->se_sym
.st_size
)
223 if (syma
->se_sym
.st_size
> symb
->se_sym
.st_size
)
227 * We really do have two identical symbols for some reason. Just report
228 * them as equal, and to the lucky one go the spoils.
234 * Construct an optimized symbol table sorted by starting address.
237 construct_symtab(dis_tgt_t
*tgt
)
243 GElf_Word
*symshndx
= NULL
;
246 sym_entry_t
*p_symtab
= NULL
;
247 int nsym
= 0; /* count of symbols we're not interested in */
250 * Find the symshndx section, if any
252 for (scn
= elf_nextscn(tgt
->dt_elf
, NULL
); scn
!= NULL
;
253 scn
= elf_nextscn(tgt
->dt_elf
, scn
)) {
254 if (gelf_getshdr(scn
, &shdr
) == NULL
)
256 if (shdr
.sh_type
== SHT_SYMTAB_SHNDX
&&
257 shdr
.sh_link
== tgt
->dt_symidx
) {
260 if ((data
= elf_getdata(scn
, NULL
)) != NULL
) {
261 symshndx
= (GElf_Word
*)data
->d_buf
;
262 symshndx_size
= data
->d_size
/
269 if ((scn
= elf_getscn(tgt
->dt_elf
, tgt
->dt_symidx
)) == NULL
)
270 die("%s: failed to get section information", tgt
->dt_filename
);
271 if (gelf_getshdr(scn
, &shdr
) == NULL
)
272 die("%s: failed to get section header", tgt
->dt_filename
);
273 if (shdr
.sh_entsize
== 0)
274 die("%s: symbol table has zero size", tgt
->dt_filename
);
276 if ((symdata
= elf_getdata(scn
, NULL
)) == NULL
)
277 die("%s: failed to get symbol table", tgt
->dt_filename
);
279 tgt
->dt_symcount
= symdata
->d_size
/ gelf_fsize(tgt
->dt_elf
, ELF_T_SYM
,
282 p_symtab
= safe_malloc(tgt
->dt_symcount
* sizeof (sym_entry_t
));
284 for (i
= 0, sym
= p_symtab
; i
< tgt
->dt_symcount
; i
++) {
285 if (gelf_getsym(symdata
, i
, &(sym
->se_sym
)) == NULL
) {
286 warn("%s: gelf_getsym returned NULL for %d",
287 tgt
->dt_filename
, i
);
293 * We're only interested in data symbols.
295 if (!IS_DATA_TYPE(GELF_ST_TYPE(sym
->se_sym
.st_info
))) {
300 if (sym
->se_sym
.st_shndx
== SHN_XINDEX
&& symshndx
!= NULL
) {
301 if (i
> symshndx_size
) {
302 warn("%s: bad SHNX_XINDEX %d",
303 tgt
->dt_filename
, i
);
306 sym
->se_shndx
= symshndx
[i
];
309 sym
->se_shndx
= sym
->se_sym
.st_shndx
;
312 /* Deal with symbols with special section indicies */
313 if (sym
->se_shndx
== SHN_ABS
) {
315 * If st_value == 0, references to these
316 * symbols in code are modified in situ
317 * thus we will never attempt to look
320 if (sym
->se_sym
.st_value
== 0) {
322 * References to these symbols in code
323 * are modified in situ by the runtime
324 * linker and no code on disk will ever
325 * attempt to look them up.
331 * If st_value != 0, (such as examining
332 * something in /system/object/.../object)
333 * the values should resolve to a value
334 * within an existing section (such as
335 * .data). This also means it never needs
336 * to have st_value mapped.
344 * Ignore the symbol if it has some other special
347 if (sym
->se_shndx
== SHN_UNDEF
||
348 sym
->se_shndx
>= SHN_LORESERVE
) {
353 if ((sym
->se_name
= elf_strptr(tgt
->dt_elf
, shdr
.sh_link
,
354 (size_t)sym
->se_sym
.st_name
)) == NULL
) {
355 warn("%s: failed to lookup symbol %d name",
356 tgt
->dt_filename
, i
);
362 * If we had to map this section, its symbol value
363 * also needs to be mapped.
365 if (tgt
->dt_shnmap
[sym
->se_shndx
].dm_mapped
)
366 sym
->se_sym
.st_value
+=
367 tgt
->dt_shnmap
[sym
->se_shndx
].dm_start
;
372 tgt
->dt_symcount
-= nsym
;
373 tgt
->dt_symtab
= realloc(p_symtab
, tgt
->dt_symcount
*
374 sizeof (sym_entry_t
));
376 qsort(tgt
->dt_symtab
, tgt
->dt_symcount
, sizeof (sym_entry_t
),
381 * Assign virtual address ranges for sections that need it
384 create_addrmap(dis_tgt_t
*tgt
)
389 if (tgt
->dt_shnmap
== NULL
)
392 /* find the greatest used address */
393 for (addr
= 0, i
= 1; i
< tgt
->dt_shncount
; i
++)
394 if (tgt
->dt_shnmap
[i
].dm_start
> addr
)
395 addr
= tgt
->dt_shnmap
[i
].dm_start
+
396 tgt
->dt_shnmap
[i
].dm_length
;
398 addr
= P2ROUNDUP(addr
, 0x1000);
401 * Assign section a starting address beyond the largest mapped section
402 * if no address was given.
404 for (i
= 1; i
< tgt
->dt_shncount
; i
++) {
405 if (tgt
->dt_shnmap
[i
].dm_start
!= 0)
408 tgt
->dt_shnmap
[i
].dm_start
= addr
;
409 tgt
->dt_shnmap
[i
].dm_mapped
= B_TRUE
;
410 addr
= P2ROUNDUP(addr
+ tgt
->dt_shnmap
[i
].dm_length
, 0x1000);
415 * Create a target backed by an ELF file.
418 dis_tgt_create(const char *file
)
420 dis_tgt_t
*tgt
, *current
;
424 Elf_Arhdr
*arhdr
= NULL
;
427 if (elf_version(EV_CURRENT
) == EV_NONE
)
428 die("libelf(3ELF) out of date");
430 tgt
= safe_malloc(sizeof (dis_tgt_t
));
432 if ((tgt
->dt_fd
= open(file
, O_RDONLY
)) < 0) {
433 warn("%s: failed opening file, reason: %s", file
,
439 if ((tgt
->dt_elf_root
=
440 elf_begin(tgt
->dt_fd
, ELF_C_READ
, NULL
)) == NULL
) {
441 warn("%s: invalid or corrupt ELF file", file
);
442 dis_tgt_destroy(tgt
);
448 while ((elf
= elf_begin(tgt
->dt_fd
, cmd
, tgt
->dt_elf_root
)) != NULL
) {
451 if (elf_kind(tgt
->dt_elf_root
) == ELF_K_AR
&&
452 (arhdr
= elf_getarhdr(elf
)) == NULL
) {
453 warn("%s: malformed archive", file
);
454 dis_tgt_destroy(tgt
);
459 * Make sure that this Elf file is sane
461 if (gelf_getehdr(elf
, &ehdr
) == NULL
) {
464 * For archives, we drive on in the face of bad
465 * members. The "/" and "//" members are
466 * special, and should be silently ignored.
468 if (strcmp(arhdr
->ar_name
, "/") != 0 &&
469 strcmp(arhdr
->ar_name
, "//") != 0)
470 warn("%s[%s]: invalid file type",
471 file
, arhdr
->ar_name
);
477 warn("%s: invalid file type", file
);
478 dis_tgt_destroy(tgt
);
483 * If we're seeing a new Elf object, then we have an
484 * archive. In this case, we create a new target, and chain it
485 * off the master target. We can later iterate over these
486 * targets using dis_tgt_next().
488 if (current
->dt_elf
!= NULL
) {
489 dis_tgt_t
*next
= safe_malloc(sizeof (dis_tgt_t
));
490 next
->dt_elf_root
= tgt
->dt_elf_root
;
492 current
->dt_next
= next
;
495 current
->dt_elf
= elf
;
496 current
->dt_arhdr
= arhdr
;
498 if (elf_getshdrstrndx(elf
, ¤t
->dt_shstrndx
) == -1) {
499 warn("%s: failed to get section string table for "
501 dis_tgt_destroy(tgt
);
505 if (elf_getshdrnum(elf
, &shnum
) == -1) {
506 warn("%s: failed to get number of sections in file",
508 dis_tgt_destroy(tgt
);
512 current
->dt_shnmap
= safe_malloc(sizeof (dis_shnmap_t
) *
514 current
->dt_shncount
= shnum
;
517 dis_tgt_section_iter(current
, tgt_scn_init
, &idx
);
518 current
->dt_filename
= file
;
520 create_addrmap(current
);
521 if (current
->dt_symidx
!= 0)
522 construct_symtab(current
);
528 * Final sanity check. If we had an archive with no members, then bail
529 * out with a nice message.
531 if (tgt
->dt_elf
== NULL
) {
532 warn("%s: empty archive\n", file
);
533 dis_tgt_destroy(tgt
);
541 * Return the filename associated with the target.
544 dis_tgt_name(dis_tgt_t
*tgt
)
546 return (tgt
->dt_filename
);
550 * Return the archive member name, if any.
553 dis_tgt_member(dis_tgt_t
*tgt
)
556 return (tgt
->dt_arhdr
->ar_name
);
562 * Return the Elf_Ehdr associated with this target. Needed to determine which
563 * disassembler to use.
566 dis_tgt_ehdr(dis_tgt_t
*tgt
, GElf_Ehdr
*ehdr
)
568 (void) gelf_getehdr(tgt
->dt_elf
, ehdr
);
572 * Return the next target in the list, if this is an archive.
575 dis_tgt_next(dis_tgt_t
*tgt
)
577 return (tgt
->dt_next
);
581 * Destroy a target and free up any associated memory.
584 dis_tgt_destroy(dis_tgt_t
*tgt
)
586 dis_tgt_t
*current
, *next
;
588 current
= tgt
->dt_next
;
589 while (current
!= NULL
) {
590 next
= current
->dt_next
;
592 (void) elf_end(current
->dt_elf
);
593 if (current
->dt_symtab
)
594 free(current
->dt_symtab
);
600 (void) elf_end(tgt
->dt_elf
);
601 if (tgt
->dt_elf_root
)
602 (void) elf_end(tgt
->dt_elf_root
);
605 free(tgt
->dt_symtab
);
611 * Given an address, return the section it is in and set the offset within
615 dis_find_section(dis_tgt_t
*tgt
, uint64_t addr
, off_t
*offset
)
619 for (i
= 1; i
< tgt
->dt_shncount
; i
++) {
620 if ((addr
>= tgt
->dt_shnmap
[i
].dm_start
) &&
621 (addr
< tgt
->dt_shnmap
[i
].dm_start
+
622 tgt
->dt_shnmap
[i
].dm_length
)) {
623 *offset
= addr
- tgt
->dt_shnmap
[i
].dm_start
;
624 return (tgt
->dt_shnmap
[i
].dm_name
);
633 * Given an address, returns the name of the corresponding symbol, as well as
634 * the offset within that symbol. If no matching symbol is found, then NULL is
637 * If 'cache_result' is specified, then we keep track of the resulting symbol.
638 * This cached result is consulted first on subsequent lookups in order to avoid
639 * unecessary lookups. This flag should be used for resolving the current PC,
640 * as the majority of addresses stay within the current function.
643 dis_tgt_lookup(dis_tgt_t
*tgt
, uint64_t addr
, off_t
*offset
, int cache_result
,
644 size_t *size
, int *isfunc
)
647 sym_entry_t
*sym
, *osym
, *match
;
655 if (tgt
->dt_symcache
!= NULL
&&
656 addr
>= tgt
->dt_symcache
->se_sym
.st_value
&&
657 addr
< tgt
->dt_symcache
->se_sym
.st_value
+
658 tgt
->dt_symcache
->se_sym
.st_size
) {
659 sym
= tgt
->dt_symcache
;
660 *offset
= addr
- sym
->se_sym
.st_value
;
661 *size
= sym
->se_sym
.st_size
;
663 *isfunc
= (GELF_ST_TYPE(sym
->se_sym
.st_info
) ==
665 return (sym
->se_name
);
669 hi
= (tgt
->dt_symcount
- 1);
675 sym
= &tgt
->dt_symtab
[mid
];
677 if (addr
>= sym
->se_sym
.st_value
&&
678 addr
< sym
->se_sym
.st_value
+ sym
->se_sym
.st_size
&&
679 (!found
|| sym
->se_sym
.st_value
> osym
->se_sym
.st_value
)) {
682 } else if (addr
== sym
->se_sym
.st_value
) {
684 * Particularly for .plt objects, it's possible to have
685 * a zero sized object. We want to return this, but we
686 * want it to be a last resort.
691 if (addr
< sym
->se_sym
.st_value
)
705 * Walk backwards to find the best match.
710 if (osym
== tgt
->dt_symtab
)
714 } while ((sym
->se_sym
.st_value
== osym
->se_sym
.st_value
) &&
715 (addr
>= osym
->se_sym
.st_value
) &&
716 (addr
< osym
->se_sym
.st_value
+ osym
->se_sym
.st_size
));
719 tgt
->dt_symcache
= sym
;
721 *offset
= addr
- sym
->se_sym
.st_value
;
722 *size
= sym
->se_sym
.st_size
;
724 *isfunc
= (GELF_ST_TYPE(sym
->se_sym
.st_info
) == STT_FUNC
);
726 return (sym
->se_name
);
730 * Given an address, return the starting offset of the next symbol in the file.
731 * Only needed on variable length instruction architectures.
734 dis_tgt_next_symbol(dis_tgt_t
*tgt
, uint64_t addr
)
738 sym
= (tgt
->dt_symcache
!= NULL
) ? tgt
->dt_symcache
: tgt
->dt_symtab
;
740 while (sym
!= (tgt
->dt_symtab
+ tgt
->dt_symcount
)) {
741 if (sym
->se_sym
.st_value
>= addr
)
742 return (sym
->se_sym
.st_value
- addr
);
750 * Iterate over all sections in the target, executing the given callback for
754 dis_tgt_section_iter(dis_tgt_t
*tgt
, section_iter_f func
, void *data
)
760 for (scn
= elf_nextscn(tgt
->dt_elf
, NULL
), idx
= 1; scn
!= NULL
;
761 scn
= elf_nextscn(tgt
->dt_elf
, scn
), idx
++) {
763 if (gelf_getshdr(scn
, &sdata
.ds_shdr
) == NULL
) {
764 warn("%s: failed to get section %d header",
765 tgt
->dt_filename
, idx
);
769 if ((sdata
.ds_name
= elf_strptr(tgt
->dt_elf
, tgt
->dt_shstrndx
,
770 sdata
.ds_shdr
.sh_name
)) == NULL
) {
771 warn("%s: failed to get section %d name",
772 tgt
->dt_filename
, idx
);
776 if ((sdata
.ds_data
= elf_getdata(scn
, NULL
)) == NULL
) {
777 warn("%s: failed to get data for section '%s'",
778 tgt
->dt_filename
, sdata
.ds_name
);
783 * dis_tgt_section_iter is also used before the section map
784 * is initialized, so only check when we need to. If the
785 * section map is uninitialized, it will return 0 and have
788 if (sdata
.ds_shdr
.sh_addr
== 0)
789 sdata
.ds_shdr
.sh_addr
= tgt
->dt_shnmap
[idx
].dm_start
;
791 func(tgt
, &sdata
, data
);
796 * Return 1 if the given section contains text, 0 otherwise.
799 dis_section_istext(dis_scn_t
*scn
)
801 return ((scn
->ds_shdr
.sh_type
== SHT_PROGBITS
) &&
802 (scn
->ds_shdr
.sh_flags
== (SHF_ALLOC
| SHF_EXECINSTR
)));
806 * Return a pointer to the section data.
809 dis_section_data(dis_scn_t
*scn
)
811 return (scn
->ds_data
->d_buf
);
815 * Return the size of the section data.
818 dis_section_size(dis_scn_t
*scn
)
820 return (scn
->ds_data
->d_size
);
824 * Return the address for the given section.
827 dis_section_addr(dis_scn_t
*scn
)
829 return (scn
->ds_shdr
.sh_addr
);
833 * Return the name of the current section.
836 dis_section_name(dis_scn_t
*scn
)
838 return (scn
->ds_name
);
842 * Create an allocated copy of the given section
845 dis_section_copy(dis_scn_t
*scn
)
849 new = safe_malloc(sizeof (dis_scn_t
));
850 (void) memcpy(new, scn
, sizeof (dis_scn_t
));
856 * Free section memory
859 dis_section_free(dis_scn_t
*scn
)
865 * Iterate over all functions in the target, executing the given callback for
869 dis_tgt_function_iter(dis_tgt_t
*tgt
, function_iter_f func
, void *data
)
877 for (i
= 0, sym
= tgt
->dt_symtab
; i
< tgt
->dt_symcount
; i
++, sym
++) {
879 /* ignore non-functions */
880 if ((GELF_ST_TYPE(sym
->se_sym
.st_info
) != STT_FUNC
) ||
881 (sym
->se_name
== NULL
) ||
882 (sym
->se_sym
.st_size
== 0) ||
883 (sym
->se_shndx
>= SHN_LORESERVE
))
886 /* get the ELF data associated with this function */
887 if ((scn
= elf_getscn(tgt
->dt_elf
, sym
->se_shndx
)) == NULL
||
888 gelf_getshdr(scn
, &shdr
) == NULL
||
889 (df
.df_data
= elf_getdata(scn
, NULL
)) == NULL
||
890 df
.df_data
->d_size
== 0) {
891 warn("%s: failed to read section %d",
892 tgt
->dt_filename
, sym
->se_shndx
);
896 if (tgt
->dt_shnmap
[sym
->se_shndx
].dm_mapped
)
897 shdr
.sh_addr
= tgt
->dt_shnmap
[sym
->se_shndx
].dm_start
;
900 * Verify that the address lies within the section that we think
903 if (sym
->se_sym
.st_value
< shdr
.sh_addr
||
904 (sym
->se_sym
.st_value
+ sym
->se_sym
.st_size
) >
905 (shdr
.sh_addr
+ shdr
.sh_size
)) {
906 warn("%s: bad section %d for address %p",
907 tgt
->dt_filename
, sym
->se_sym
.st_shndx
,
908 sym
->se_sym
.st_value
);
913 df
.df_offset
= sym
->se_sym
.st_value
- shdr
.sh_addr
;
915 func(tgt
, &df
, data
);
920 * Return the data associated with a given function.
923 dis_function_data(dis_func_t
*func
)
925 return ((char *)func
->df_data
->d_buf
+ func
->df_offset
);
929 * Return the size of a function.
932 dis_function_size(dis_func_t
*func
)
934 return (func
->df_sym
->se_sym
.st_size
);
938 * Return the address of a function.
941 dis_function_addr(dis_func_t
*func
)
943 return (func
->df_sym
->se_sym
.st_value
);
947 * Return the name of the function
950 dis_function_name(dis_func_t
*func
)
952 return (func
->df_sym
->se_name
);
956 * Return a copy of a function.
959 dis_function_copy(dis_func_t
*func
)
963 new = safe_malloc(sizeof (dis_func_t
));
964 (void) memcpy(new, func
, sizeof (dis_func_t
));
970 * Free function memory
973 dis_function_free(dis_func_t
*func
)