dis_target.c revision b5f3c6ffe7f93e6132a702a851a69b5ecd78c066
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 * Copyright 2011 Jason King.  All rights reserved.
26 */
27
28#include <assert.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <gelf.h>
32#include <libelf.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36
37#include <sys/fcntl.h>
38#include <sys/stat.h>
39#include <sys/sysmacros.h>
40#include <sys/types.h>
41
42#include "dis_target.h"
43#include "dis_util.h"
44
45/*
46 * Standard ELF disassembler target.
47 *
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.
52 */
53
54/*
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.
57 */
58typedef 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 */
62} sym_entry_t;
63
64/*
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).
69 */
70typedef 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 */
75} dis_shnmap_t;
76
77/*
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.
82 */
83struct dis_tgt {
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 */
97};
98
99/*
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.
103 */
104struct dis_func {
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 */
108};
109
110/*
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.
114 */
115struct dis_scn {
116	GElf_Shdr	ds_shdr;
117	const char	*ds_name;
118	Elf_Data	*ds_data;
119};
120
121/* Lifted from Psymtab.c, omitting STT_TLS */
122#define	DATA_TYPES      \
123	((1 << STT_OBJECT) | (1 << STT_FUNC) | (1 << STT_COMMON))
124#define	IS_DATA_TYPE(tp)	(((1 << (tp)) & DATA_TYPES) != 0)
125
126/*
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
129 * over SHT_DYNSYM.
130 */
131/* ARGSUSED */
132static void
133tgt_scn_init(dis_tgt_t *tgt, dis_scn_t *scn, void *data)
134{
135	int *index = data;
136
137	*index += 1;
138
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;
143
144	/*
145	 * Prefer SHT_SYMTAB over SHT_DYNSYM
146	 */
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;
151}
152
153static int
154sym_compare(const void *a, const void *b)
155{
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;
160
161	if (syma->se_sym.st_value < symb->se_sym.st_value)
162		return (-1);
163
164	if (syma->se_sym.st_value > symb->se_sym.st_value)
165		return (1);
166
167	/*
168	 * Prefer functions over non-functions
169	 */
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)
173			return (-1);
174		if (GELF_ST_TYPE(symb->se_sym.st_info) == STT_FUNC)
175			return (1);
176	}
177
178	/*
179	 * For symbols with the same address and type, we sort them according to
180	 * a hierarchy:
181	 *
182	 * 	1. weak symbols (common name)
183	 * 	2. global symbols (external name)
184	 * 	3. local symbols
185	 */
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)
189			return (-1);
190		if (GELF_ST_BIND(symb->se_sym.st_info) == STB_WEAK)
191			return (1);
192
193		if (GELF_ST_BIND(syma->se_sym.st_info) == STB_GLOBAL)
194			return (-1);
195		if (GELF_ST_BIND(symb->se_sym.st_info) == STB_GLOBAL)
196			return (1);
197	}
198
199	/*
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.
202	 */
203	if (aname == NULL)
204		return (-1);
205	if (bname == NULL)
206		return (1);
207
208	while (*aname == '_' && *bname == '_') {
209		aname++;
210		bname++;
211	}
212
213	if (*bname == '_')
214		return (-1);
215	if (*aname == '_')
216		return (1);
217
218	/*
219	 * Prefer the symbol with the smaller size.
220	 */
221	if (syma->se_sym.st_size < symb->se_sym.st_size)
222		return (-1);
223	if (syma->se_sym.st_size > symb->se_sym.st_size)
224		return (1);
225
226	/*
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.
229	 */
230	return (0);
231}
232
233/*
234 * Construct an optimized symbol table sorted by starting address.
235 */
236static void
237construct_symtab(dis_tgt_t *tgt)
238{
239	Elf_Scn *scn;
240	GElf_Shdr shdr;
241	Elf_Data *symdata;
242	int i;
243	GElf_Word *symshndx = NULL;
244	int symshndx_size;
245	sym_entry_t *sym;
246	sym_entry_t *p_symtab = NULL;
247	int nsym = 0; /* count of symbols we're not interested in */
248
249	/*
250	 * Find the symshndx section, if any
251	 */
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)
255			break;
256		if (shdr.sh_type == SHT_SYMTAB_SHNDX &&
257		    shdr.sh_link == tgt->dt_symidx) {
258			Elf_Data	*data;
259
260			if ((data = elf_getdata(scn, NULL)) != NULL) {
261				symshndx = (GElf_Word *)data->d_buf;
262				symshndx_size = data->d_size /
263				    sizeof (GElf_Word);
264				break;
265			}
266		}
267	}
268
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);
275
276	if ((symdata = elf_getdata(scn, NULL)) == NULL)
277		die("%s: failed to get symbol table", tgt->dt_filename);
278
279	tgt->dt_symcount = symdata->d_size / gelf_fsize(tgt->dt_elf, ELF_T_SYM,
280	    1, EV_CURRENT);
281
282	p_symtab = safe_malloc(tgt->dt_symcount * sizeof (sym_entry_t));
283
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);
288			nsym++;
289			continue;
290		}
291
292		/*
293		 * We're only interested in data symbols.
294		 */
295		if (!IS_DATA_TYPE(GELF_ST_TYPE(sym->se_sym.st_info))) {
296			nsym++;
297			continue;
298		}
299
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);
304				sym->se_shndx = -1;
305			} else {
306				sym->se_shndx = symshndx[i];
307			}
308		} else {
309			sym->se_shndx = sym->se_sym.st_shndx;
310		}
311
312		if ((sym->se_name = elf_strptr(tgt->dt_elf, shdr.sh_link,
313		    (size_t)sym->se_sym.st_name)) == NULL) {
314			warn("%s: failed to lookup symbol %d name",
315			    tgt->dt_filename, i);
316			nsym++;
317			continue;
318		}
319
320		/*
321		 * If we had to map this section, its symbol value
322		 * also needs to be mapped.
323		 */
324		if (tgt->dt_shnmap[sym->se_shndx].dm_mapped)
325			sym->se_sym.st_value +=
326			    tgt->dt_shnmap[sym->se_shndx].dm_start;
327
328		sym++;
329	}
330
331	tgt->dt_symcount -= nsym;
332	tgt->dt_symtab = realloc(p_symtab, tgt->dt_symcount *
333	    sizeof (sym_entry_t));
334
335	qsort(tgt->dt_symtab, tgt->dt_symcount, sizeof (sym_entry_t),
336	    sym_compare);
337}
338
339/*
340 * Assign virtual address ranges for sections that need it
341 */
342static void
343create_addrmap(dis_tgt_t *tgt)
344{
345	uint64_t addr;
346	int i;
347
348	if (tgt->dt_shnmap == NULL)
349		return;
350
351	/* find the greatest used address */
352	for (addr = 0, i = 1; i < tgt->dt_shncount; i++)
353		if (tgt->dt_shnmap[i].dm_start > addr)
354			addr = tgt->dt_shnmap[i].dm_start +
355			    tgt->dt_shnmap[i].dm_length;
356
357	addr = P2ROUNDUP(addr, 0x1000);
358
359	/*
360	 * Assign section a starting address beyond the largest mapped section
361	 * if no address was given.
362	 */
363	for (i = 1; i < tgt->dt_shncount; i++) {
364		if (tgt->dt_shnmap[i].dm_start != 0)
365			continue;
366
367		tgt->dt_shnmap[i].dm_start = addr;
368		tgt->dt_shnmap[i].dm_mapped = B_TRUE;
369		addr = P2ROUNDUP(addr + tgt->dt_shnmap[i].dm_length, 0x1000);
370	}
371}
372
373/*
374 * Create a target backed by an ELF file.
375 */
376dis_tgt_t *
377dis_tgt_create(const char *file)
378{
379	dis_tgt_t *tgt, *current;
380	int idx;
381	Elf *elf;
382	GElf_Ehdr ehdr;
383	Elf_Arhdr *arhdr = NULL;
384	int cmd;
385
386	if (elf_version(EV_CURRENT) == EV_NONE)
387		die("libelf(3ELF) out of date");
388
389	tgt = safe_malloc(sizeof (dis_tgt_t));
390
391	if ((tgt->dt_fd = open(file, O_RDONLY)) < 0) {
392		warn("%s: failed opening file, reason: %s", file,
393		    strerror(errno));
394		free(tgt);
395		return (NULL);
396	}
397
398	if ((tgt->dt_elf_root =
399	    elf_begin(tgt->dt_fd, ELF_C_READ, NULL)) == NULL) {
400		warn("%s: invalid or corrupt ELF file", file);
401		dis_tgt_destroy(tgt);
402		return (NULL);
403	}
404
405	current = tgt;
406	cmd = ELF_C_READ;
407	while ((elf = elf_begin(tgt->dt_fd, cmd, tgt->dt_elf_root)) != NULL) {
408
409		if (elf_kind(tgt->dt_elf_root) == ELF_K_AR &&
410		    (arhdr = elf_getarhdr(elf)) == NULL) {
411			warn("%s: malformed archive", file);
412			dis_tgt_destroy(tgt);
413			return (NULL);
414		}
415
416		/*
417		 * Make sure that this Elf file is sane
418		 */
419		if (gelf_getehdr(elf, &ehdr) == NULL) {
420			if (arhdr != NULL) {
421				/*
422				 * For archives, we drive on in the face of bad
423				 * members.  The "/" and "//" members are
424				 * special, and should be silently ignored.
425				 */
426				if (strcmp(arhdr->ar_name, "/") != 0 &&
427				    strcmp(arhdr->ar_name, "//") != 0)
428					warn("%s[%s]: invalid file type",
429					    file, arhdr->ar_name);
430				cmd = elf_next(elf);
431				(void) elf_end(elf);
432				continue;
433			}
434
435			warn("%s: invalid file type", file);
436			dis_tgt_destroy(tgt);
437			return (NULL);
438		}
439
440		/*
441		 * If we're seeing a new Elf object, then we have an
442		 * archive. In this case, we create a new target, and chain it
443		 * off the master target.  We can later iterate over these
444		 * targets using dis_tgt_next().
445		 */
446		if (current->dt_elf != NULL) {
447			dis_tgt_t *next = safe_malloc(sizeof (dis_tgt_t));
448			next->dt_elf_root = tgt->dt_elf_root;
449			next->dt_fd = -1;
450			current->dt_next = next;
451			current = next;
452		}
453		current->dt_elf = elf;
454		current->dt_arhdr = arhdr;
455
456		if (elf_getshdrstrndx(elf, &current->dt_shstrndx) == -1) {
457			warn("%s: failed to get section string table for "
458			    "file", file);
459			dis_tgt_destroy(tgt);
460			return (NULL);
461		}
462
463		current->dt_shnmap = safe_malloc(sizeof (dis_shnmap_t) *
464		    ehdr.e_shnum);
465		current->dt_shncount = ehdr.e_shnum;
466
467		idx = 0;
468		dis_tgt_section_iter(current, tgt_scn_init, &idx);
469
470		create_addrmap(current);
471		if (current->dt_symidx != 0)
472			construct_symtab(current);
473
474		current->dt_filename = file;
475
476		cmd = elf_next(elf);
477	}
478
479	/*
480	 * Final sanity check.  If we had an archive with no members, then bail
481	 * out with a nice message.
482	 */
483	if (tgt->dt_elf == NULL) {
484		warn("%s: empty archive\n", file);
485		dis_tgt_destroy(tgt);
486		return (NULL);
487	}
488
489	return (tgt);
490}
491
492/*
493 * Return the filename associated with the target.
494 */
495const char *
496dis_tgt_name(dis_tgt_t *tgt)
497{
498	return (tgt->dt_filename);
499}
500
501/*
502 * Return the archive member name, if any.
503 */
504const char *
505dis_tgt_member(dis_tgt_t *tgt)
506{
507	if (tgt->dt_arhdr)
508		return (tgt->dt_arhdr->ar_name);
509	else
510		return (NULL);
511}
512
513/*
514 * Return the Elf_Ehdr associated with this target.  Needed to determine which
515 * disassembler to use.
516 */
517void
518dis_tgt_ehdr(dis_tgt_t *tgt, GElf_Ehdr *ehdr)
519{
520	(void) gelf_getehdr(tgt->dt_elf, ehdr);
521}
522
523/*
524 * Return the next target in the list, if this is an archive.
525 */
526dis_tgt_t *
527dis_tgt_next(dis_tgt_t *tgt)
528{
529	return (tgt->dt_next);
530}
531
532/*
533 * Destroy a target and free up any associated memory.
534 */
535void
536dis_tgt_destroy(dis_tgt_t *tgt)
537{
538	dis_tgt_t *current, *next;
539
540	current = tgt->dt_next;
541	while (current != NULL) {
542		next = current->dt_next;
543		if (current->dt_elf)
544			(void) elf_end(current->dt_elf);
545		if (current->dt_symtab)
546			free(current->dt_symtab);
547		free(current);
548		current = next;
549	}
550
551	if (tgt->dt_elf)
552		(void) elf_end(tgt->dt_elf);
553	if (tgt->dt_elf_root)
554		(void) elf_end(tgt->dt_elf_root);
555
556	if (tgt->dt_symtab)
557		free(tgt->dt_symtab);
558
559	free(tgt);
560}
561
562/*
563 * Given an address, return the section it is in and set the offset within
564 * the section.
565 */
566const char *
567dis_find_section(dis_tgt_t *tgt, uint64_t addr, off_t *offset)
568{
569	int i;
570
571	for (i = 1; i < tgt->dt_shncount; i++) {
572		if ((addr >= tgt->dt_shnmap[i].dm_start) &&
573		    (addr < tgt->dt_shnmap[i].dm_start +
574		    tgt->dt_shnmap[i].dm_length)) {
575			*offset = addr - tgt->dt_shnmap[i].dm_start;
576			return (tgt->dt_shnmap[i].dm_name);
577		}
578	}
579
580	*offset = 0;
581	return (NULL);
582}
583
584/*
585 * Given an address, returns the name of the corresponding symbol, as well as
586 * the offset within that symbol.  If no matching symbol is found, then NULL is
587 * returned.
588 *
589 * If 'cache_result' is specified, then we keep track of the resulting symbol.
590 * This cached result is consulted first on subsequent lookups in order to avoid
591 * unecessary lookups.  This flag should be used for resolving the current PC,
592 * as the majority of addresses stay within the current function.
593 */
594const char *
595dis_tgt_lookup(dis_tgt_t *tgt, uint64_t addr, off_t *offset, int cache_result,
596    size_t *size, int *isfunc)
597{
598	int lo, hi, mid;
599	sym_entry_t *sym, *osym, *match;
600	int found;
601
602	if (tgt->dt_symcache != NULL &&
603	    addr >= tgt->dt_symcache->se_sym.st_value &&
604	    addr < tgt->dt_symcache->se_sym.st_value +
605	    tgt->dt_symcache->se_sym.st_size) {
606		*offset = addr - tgt->dt_symcache->se_sym.st_value;
607		*size = tgt->dt_symcache->se_sym.st_size;
608		return (tgt->dt_symcache->se_name);
609	}
610
611	lo = 0;
612	hi = (tgt->dt_symcount - 1);
613	found = 0;
614	match = osym = NULL;
615	while (lo <= hi) {
616		mid = (lo + hi) / 2;
617
618		sym = &tgt->dt_symtab[mid];
619
620		if (addr >= sym->se_sym.st_value &&
621		    addr < sym->se_sym.st_value + sym->se_sym.st_size &&
622		    (!found || sym->se_sym.st_value > osym->se_sym.st_value)) {
623			osym = sym;
624			found = 1;
625		} else if (addr == sym->se_sym.st_value) {
626			/*
627			 * Particularly for .plt objects, it's possible to have
628			 * a zero sized object.  We want to return this, but we
629			 * want it to be a last resort.
630			 */
631			match = sym;
632		}
633
634		if (addr < sym->se_sym.st_value)
635			hi = mid - 1;
636		else
637			lo = mid + 1;
638	}
639
640	if (!found) {
641		if (match)
642			osym = match;
643		else
644			return (NULL);
645	}
646
647	/*
648	 * Walk backwards to find the best match.
649	 */
650	do {
651		sym = osym;
652
653		if (osym == tgt->dt_symtab)
654			break;
655
656		osym = osym - 1;
657	} while ((sym->se_sym.st_value == osym->se_sym.st_value) &&
658	    (addr >= osym->se_sym.st_value) &&
659	    (addr < osym->se_sym.st_value + osym->se_sym.st_size));
660
661	if (cache_result)
662		tgt->dt_symcache = sym;
663
664	*offset = addr - sym->se_sym.st_value;
665	*size = sym->se_sym.st_size;
666	if (isfunc)
667		*isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) == STT_FUNC);
668
669	return (sym->se_name);
670}
671
672/*
673 * Given an address, return the starting offset of the next symbol in the file.
674 */
675off_t
676dis_tgt_next_symbol(dis_tgt_t *tgt, uint64_t addr)
677{
678	sym_entry_t *sym;
679
680	for (sym = tgt->dt_symcache;
681	    sym != tgt->dt_symtab + tgt->dt_symcount;
682	    sym++) {
683		if (sym->se_sym.st_value >= addr)
684			return (sym->se_sym.st_value - addr);
685	}
686
687	return (0);
688}
689
690/*
691 * Iterate over all sections in the target, executing the given callback for
692 * each.
693 */
694void
695dis_tgt_section_iter(dis_tgt_t *tgt, section_iter_f func, void *data)
696{
697	dis_scn_t sdata;
698	Elf_Scn *scn;
699	int idx;
700
701	for (scn = elf_nextscn(tgt->dt_elf, NULL), idx = 1; scn != NULL;
702	    scn = elf_nextscn(tgt->dt_elf, scn), idx++) {
703
704		if (gelf_getshdr(scn, &sdata.ds_shdr) == NULL) {
705			warn("%s: failed to get section %d header",
706			    tgt->dt_filename, idx);
707			continue;
708		}
709
710		if ((sdata.ds_name = elf_strptr(tgt->dt_elf, tgt->dt_shstrndx,
711		    sdata.ds_shdr.sh_name)) == NULL) {
712			warn("%s: failed to get section %d name",
713			    tgt->dt_filename, idx);
714			continue;
715		}
716
717		if ((sdata.ds_data = elf_getdata(scn, NULL)) == NULL) {
718			warn("%s: failed to get data for section '%s'",
719			    tgt->dt_filename, sdata.ds_name);
720			continue;
721		}
722
723		/*
724		 * dis_tgt_section_iter is also used before the section map
725		 * is initialized, so only check when we need to.  If the
726		 * section map is uninitialized, it will return 0 and have
727		 * no net effect.
728		 */
729		if (sdata.ds_shdr.sh_addr == 0)
730			sdata.ds_shdr.sh_addr = tgt->dt_shnmap[idx].dm_start;
731
732		func(tgt, &sdata, data);
733	}
734}
735
736/*
737 * Return 1 if the given section contains text, 0 otherwise.
738 */
739int
740dis_section_istext(dis_scn_t *scn)
741{
742	return ((scn->ds_shdr.sh_type == SHT_PROGBITS) &&
743	    (scn->ds_shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR)));
744}
745
746/*
747 * Return a pointer to the section data.
748 */
749void *
750dis_section_data(dis_scn_t *scn)
751{
752	return (scn->ds_data->d_buf);
753}
754
755/*
756 * Return the size of the section data.
757 */
758size_t
759dis_section_size(dis_scn_t *scn)
760{
761	return (scn->ds_data->d_size);
762}
763
764/*
765 * Return the address for the given section.
766 */
767uint64_t
768dis_section_addr(dis_scn_t *scn)
769{
770	return (scn->ds_shdr.sh_addr);
771}
772
773/*
774 * Return the name of the current section.
775 */
776const char *
777dis_section_name(dis_scn_t *scn)
778{
779	return (scn->ds_name);
780}
781
782/*
783 * Create an allocated copy of the given section
784 */
785dis_scn_t *
786dis_section_copy(dis_scn_t *scn)
787{
788	dis_scn_t *new;
789
790	new = safe_malloc(sizeof (dis_scn_t));
791	(void) memcpy(new, scn, sizeof (dis_scn_t));
792
793	return (new);
794}
795
796/*
797 * Free section memory
798 */
799void
800dis_section_free(dis_scn_t *scn)
801{
802	free(scn);
803}
804
805/*
806 * Iterate over all functions in the target, executing the given callback for
807 * each one.
808 */
809void
810dis_tgt_function_iter(dis_tgt_t *tgt, function_iter_f func, void *data)
811{
812	int i;
813	sym_entry_t *sym;
814	dis_func_t df;
815	Elf_Scn *scn;
816	GElf_Shdr	shdr;
817
818	for (i = 0, sym = tgt->dt_symtab; i < tgt->dt_symcount; i++, sym++) {
819
820		/* ignore non-functions */
821		if ((GELF_ST_TYPE(sym->se_sym.st_info) != STT_FUNC) ||
822		    (sym->se_name == NULL) ||
823		    (sym->se_sym.st_size == 0) ||
824		    (sym->se_shndx >= SHN_LORESERVE))
825			continue;
826
827		/* get the ELF data associated with this function */
828		if ((scn = elf_getscn(tgt->dt_elf, sym->se_shndx)) == NULL ||
829		    gelf_getshdr(scn, &shdr) == NULL ||
830		    (df.df_data = elf_getdata(scn, NULL)) == NULL ||
831		    df.df_data->d_size == 0) {
832			warn("%s: failed to read section %d",
833			    tgt->dt_filename, sym->se_shndx);
834			continue;
835		}
836
837		if (tgt->dt_shnmap[sym->se_shndx].dm_mapped)
838			shdr.sh_addr = tgt->dt_shnmap[sym->se_shndx].dm_start;
839
840		/*
841		 * Verify that the address lies within the section that we think
842		 * it does.
843		 */
844		if (sym->se_sym.st_value < shdr.sh_addr ||
845		    (sym->se_sym.st_value + sym->se_sym.st_size) >
846		    (shdr.sh_addr + shdr.sh_size)) {
847			warn("%s: bad section %d for address %p",
848			    tgt->dt_filename, sym->se_sym.st_shndx,
849			    sym->se_sym.st_value);
850			continue;
851		}
852
853		df.df_sym = sym;
854		df.df_offset = sym->se_sym.st_value - shdr.sh_addr;
855
856		func(tgt, &df, data);
857	}
858}
859
860/*
861 * Return the data associated with a given function.
862 */
863void *
864dis_function_data(dis_func_t *func)
865{
866	return ((char *)func->df_data->d_buf + func->df_offset);
867}
868
869/*
870 * Return the size of a function.
871 */
872size_t
873dis_function_size(dis_func_t *func)
874{
875	return (func->df_sym->se_sym.st_size);
876}
877
878/*
879 * Return the address of a function.
880 */
881uint64_t
882dis_function_addr(dis_func_t *func)
883{
884	return (func->df_sym->se_sym.st_value);
885}
886
887/*
888 * Return the name of the function
889 */
890const char *
891dis_function_name(dis_func_t *func)
892{
893	return (func->df_sym->se_name);
894}
895
896/*
897 * Return a copy of a function.
898 */
899dis_func_t *
900dis_function_copy(dis_func_t *func)
901{
902	dis_func_t *new;
903
904	new = safe_malloc(sizeof (dis_func_t));
905	(void) memcpy(new, func, sizeof (dis_func_t));
906
907	return (new);
908}
909
910/*
911 * Free function memory
912 */
913void
914dis_function_free(dis_func_t *func)
915{
916	free(func);
917}
918