xref: /titanic_51/usr/src/cmd/dis/dis_target.c (revision dc986d9f4cb40f3ff88fe0d9c9fb3a51ad50f0d9)
1dc0093f4Seschrock /*
2dc0093f4Seschrock  * CDDL HEADER START
3dc0093f4Seschrock  *
4dc0093f4Seschrock  * The contents of this file are subject to the terms of the
5dc0093f4Seschrock  * Common Development and Distribution License (the "License").
6dc0093f4Seschrock  * You may not use this file except in compliance with the License.
7dc0093f4Seschrock  *
8dc0093f4Seschrock  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9dc0093f4Seschrock  * or http://www.opensolaris.org/os/licensing.
10dc0093f4Seschrock  * See the License for the specific language governing permissions
11dc0093f4Seschrock  * and limitations under the License.
12dc0093f4Seschrock  *
13dc0093f4Seschrock  * When distributing Covered Code, include this CDDL HEADER in each
14dc0093f4Seschrock  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15dc0093f4Seschrock  * If applicable, add the following below this CDDL HEADER, with the
16dc0093f4Seschrock  * fields enclosed by brackets "[]" replaced with your own identifying
17dc0093f4Seschrock  * information: Portions Copyright [yyyy] [name of copyright owner]
18dc0093f4Seschrock  *
19dc0093f4Seschrock  * CDDL HEADER END
20dc0093f4Seschrock  */
21dc0093f4Seschrock 
22dc0093f4Seschrock /*
2323a1cceaSRoger A. Faulkner  * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
24b5f3c6ffSJason King  *
25b5f3c6ffSJason King  * Copyright 2011 Jason King.  All rights reserved.
26dc0093f4Seschrock  */
27dc0093f4Seschrock 
28dc0093f4Seschrock #include <assert.h>
29dc0093f4Seschrock #include <errno.h>
30dc0093f4Seschrock #include <fcntl.h>
31dc0093f4Seschrock #include <gelf.h>
32dc0093f4Seschrock #include <libelf.h>
33dc0093f4Seschrock #include <stdlib.h>
34dc0093f4Seschrock #include <string.h>
35dc0093f4Seschrock #include <unistd.h>
36dc0093f4Seschrock 
37dc0093f4Seschrock #include <sys/fcntl.h>
38dc0093f4Seschrock #include <sys/stat.h>
39b5f3c6ffSJason King #include <sys/sysmacros.h>
40b5f3c6ffSJason King #include <sys/types.h>
41dc0093f4Seschrock 
42dc0093f4Seschrock #include "dis_target.h"
43dc0093f4Seschrock #include "dis_util.h"
44dc0093f4Seschrock 
45dc0093f4Seschrock /*
46dc0093f4Seschrock  * Standard ELF disassembler target.
47dc0093f4Seschrock  *
48dc0093f4Seschrock  * We only support disassembly of ELF files, though this target interface could
49dc0093f4Seschrock  * be extended in the future.  Each basic type (target, func, section) contains
50dc0093f4Seschrock  * enough information to uniquely identify the location within the file.  The
51dc0093f4Seschrock  * interfaces use libelf(3LIB) to do the actual processing of the file.
52dc0093f4Seschrock  */
53dc0093f4Seschrock 
54dc0093f4Seschrock /*
55dc0093f4Seschrock  * Symbol table entry type.  We maintain our own symbol table sorted by address,
56dc0093f4Seschrock  * with the symbol name already resolved against the ELF symbol table.
57dc0093f4Seschrock  */
58dc0093f4Seschrock typedef struct sym_entry {
59dc0093f4Seschrock 	GElf_Sym	se_sym;		/* value of symbol */
60dc0093f4Seschrock 	char		*se_name;	/* name of symbol */
61dc0093f4Seschrock 	int		se_shndx;	/* section where symbol is located */
62dc0093f4Seschrock } sym_entry_t;
63dc0093f4Seschrock 
64dc0093f4Seschrock /*
65b5f3c6ffSJason King  * Create a map of the virtual address ranges of every section.  This will
66b5f3c6ffSJason King  * allow us to create dummpy mappings for unassigned addresses.  Otherwise
67b5f3c6ffSJason King  * multiple sections with unassigned addresses will appear to overlap and
68b5f3c6ffSJason King  * mess up symbol resolution (which uses the virtual address).
69b5f3c6ffSJason King  */
70b5f3c6ffSJason King typedef struct dis_shnmap {
71b5f3c6ffSJason King 	const char 	*dm_name;	/* name of section */
72b5f3c6ffSJason King 	uint64_t	dm_start;	/* virtual address of section */
73b5f3c6ffSJason King 	size_t		dm_length;	/* address length */
74b5f3c6ffSJason King 	boolean_t	dm_mapped;	/* did we assign the mapping */
75b5f3c6ffSJason King } dis_shnmap_t;
76b5f3c6ffSJason King 
77b5f3c6ffSJason King /*
78dc0093f4Seschrock  * Target data structure.  This structure keeps track of the ELF file
79dc0093f4Seschrock  * information, a few bits of pre-processed section index information, and
80dc0093f4Seschrock  * sorted versions of the symbol table.  We also keep track of the last symbol
81dc0093f4Seschrock  * looked up, as the majority of lookups remain within the same symbol.
82dc0093f4Seschrock  */
83dc0093f4Seschrock struct dis_tgt {
84dc0093f4Seschrock 	Elf		*dt_elf;	/* libelf handle */
85dc0093f4Seschrock 	Elf		*dt_elf_root;	/* main libelf handle (for archives) */
86dc0093f4Seschrock 	const char	*dt_filename;	/* name of file */
87dc0093f4Seschrock 	int		dt_fd;		/* underlying file descriptor */
88dc0093f4Seschrock 	size_t		dt_shstrndx;	/* section index of .shstrtab */
89dc0093f4Seschrock 	size_t		dt_symidx;	/* section index of symbol table */
90dc0093f4Seschrock 	sym_entry_t	*dt_symcache;	/* last symbol looked up */
91dc0093f4Seschrock 	sym_entry_t	*dt_symtab;	/* sorted symbol table */
92dc0093f4Seschrock 	int		dt_symcount;	/* # of symbol table entries */
93dc0093f4Seschrock 	struct dis_tgt	*dt_next;	/* next target (for archives) */
94dc0093f4Seschrock 	Elf_Arhdr	*dt_arhdr;	/* archive header (for archives) */
95b5f3c6ffSJason King 	dis_shnmap_t	*dt_shnmap;	/* section address map */
96b5f3c6ffSJason King 	size_t		dt_shncount;	/* # of sections in target */
97dc0093f4Seschrock };
98dc0093f4Seschrock 
99dc0093f4Seschrock /*
100dc0093f4Seschrock  * Function data structure.  We resolve the symbol and lookup the associated ELF
101dc0093f4Seschrock  * data when building this structure.  The offset is calculated based on the
102dc0093f4Seschrock  * section's starting address.
103dc0093f4Seschrock  */
104dc0093f4Seschrock struct dis_func {
105dc0093f4Seschrock 	sym_entry_t	*df_sym;	/* symbol table reference */
106dc0093f4Seschrock 	Elf_Data	*df_data;	/* associated ELF data */
107dc0093f4Seschrock 	size_t		df_offset;	/* offset within data */
108dc0093f4Seschrock };
109dc0093f4Seschrock 
110dc0093f4Seschrock /*
111dc0093f4Seschrock  * Section data structure.  We store the entire section header so that we can
112dc0093f4Seschrock  * determine some properties (such as whether or not it contains text) after
113dc0093f4Seschrock  * building the structure.
114dc0093f4Seschrock  */
115dc0093f4Seschrock struct dis_scn {
116dc0093f4Seschrock 	GElf_Shdr	ds_shdr;
117dc0093f4Seschrock 	const char	*ds_name;
118dc0093f4Seschrock 	Elf_Data	*ds_data;
119dc0093f4Seschrock };
120dc0093f4Seschrock 
12123a1cceaSRoger A. Faulkner /* Lifted from Psymtab.c, omitting STT_TLS */
122dc0093f4Seschrock #define	DATA_TYPES      \
12323a1cceaSRoger A. Faulkner 	((1 << STT_OBJECT) | (1 << STT_FUNC) | (1 << STT_COMMON))
124dc0093f4Seschrock #define	IS_DATA_TYPE(tp)	(((1 << (tp)) & DATA_TYPES) != 0)
125dc0093f4Seschrock 
126dc0093f4Seschrock /*
127b5f3c6ffSJason King  * Save the virtual address range for this section and select the
128b5f3c6ffSJason King  * best section to use as the symbol table.  We prefer SHT_SYMTAB
129b5f3c6ffSJason King  * over SHT_DYNSYM.
130dc0093f4Seschrock  */
131dc0093f4Seschrock /* ARGSUSED */
132dc0093f4Seschrock static void
133b5f3c6ffSJason King tgt_scn_init(dis_tgt_t *tgt, dis_scn_t *scn, void *data)
134dc0093f4Seschrock {
135dc0093f4Seschrock 	int *index = data;
136dc0093f4Seschrock 
137dc0093f4Seschrock 	*index += 1;
138dc0093f4Seschrock 
139b5f3c6ffSJason King 	tgt->dt_shnmap[*index].dm_name = scn->ds_name;
140b5f3c6ffSJason King 	tgt->dt_shnmap[*index].dm_start = scn->ds_shdr.sh_addr;
141b5f3c6ffSJason King 	tgt->dt_shnmap[*index].dm_length = scn->ds_shdr.sh_size;
142b5f3c6ffSJason King 	tgt->dt_shnmap[*index].dm_mapped = B_FALSE;
143b5f3c6ffSJason King 
144dc0093f4Seschrock 	/*
145dc0093f4Seschrock 	 * Prefer SHT_SYMTAB over SHT_DYNSYM
146dc0093f4Seschrock 	 */
147dc0093f4Seschrock 	if (scn->ds_shdr.sh_type == SHT_DYNSYM && tgt->dt_symidx == 0)
148dc0093f4Seschrock 		tgt->dt_symidx = *index;
149dc0093f4Seschrock 	else if (scn->ds_shdr.sh_type == SHT_SYMTAB)
150dc0093f4Seschrock 		tgt->dt_symidx = *index;
151dc0093f4Seschrock }
152dc0093f4Seschrock 
153dc0093f4Seschrock static int
154dc0093f4Seschrock sym_compare(const void *a, const void *b)
155dc0093f4Seschrock {
156dc0093f4Seschrock 	const sym_entry_t *syma = a;
157dc0093f4Seschrock 	const sym_entry_t *symb = b;
158dc0093f4Seschrock 	const char *aname = syma->se_name;
159dc0093f4Seschrock 	const char *bname = symb->se_name;
160dc0093f4Seschrock 
161dc0093f4Seschrock 	if (syma->se_sym.st_value < symb->se_sym.st_value)
162dc0093f4Seschrock 		return (-1);
163dc0093f4Seschrock 
164dc0093f4Seschrock 	if (syma->se_sym.st_value > symb->se_sym.st_value)
165dc0093f4Seschrock 		return (1);
166dc0093f4Seschrock 
167dc0093f4Seschrock 	/*
168dc0093f4Seschrock 	 * Prefer functions over non-functions
169dc0093f4Seschrock 	 */
170dc0093f4Seschrock 	if (GELF_ST_TYPE(syma->se_sym.st_info) !=
171dc0093f4Seschrock 	    GELF_ST_TYPE(symb->se_sym.st_info)) {
172dc0093f4Seschrock 		if (GELF_ST_TYPE(syma->se_sym.st_info) == STT_FUNC)
173dc0093f4Seschrock 			return (-1);
174dc0093f4Seschrock 		if (GELF_ST_TYPE(symb->se_sym.st_info) == STT_FUNC)
175dc0093f4Seschrock 			return (1);
176dc0093f4Seschrock 	}
177dc0093f4Seschrock 
178dc0093f4Seschrock 	/*
179dc0093f4Seschrock 	 * For symbols with the same address and type, we sort them according to
180dc0093f4Seschrock 	 * a hierarchy:
181dc0093f4Seschrock 	 *
182dc0093f4Seschrock 	 * 	1. weak symbols (common name)
183dc0093f4Seschrock 	 * 	2. global symbols (external name)
184dc0093f4Seschrock 	 * 	3. local symbols
185dc0093f4Seschrock 	 */
186dc0093f4Seschrock 	if (GELF_ST_BIND(syma->se_sym.st_info) !=
187dc0093f4Seschrock 	    GELF_ST_BIND(symb->se_sym.st_info)) {
188dc0093f4Seschrock 		if (GELF_ST_BIND(syma->se_sym.st_info) == STB_WEAK)
189dc0093f4Seschrock 			return (-1);
190dc0093f4Seschrock 		if (GELF_ST_BIND(symb->se_sym.st_info) == STB_WEAK)
191dc0093f4Seschrock 			return (1);
192dc0093f4Seschrock 
193dc0093f4Seschrock 		if (GELF_ST_BIND(syma->se_sym.st_info) == STB_GLOBAL)
194dc0093f4Seschrock 			return (-1);
195dc0093f4Seschrock 		if (GELF_ST_BIND(symb->se_sym.st_info) == STB_GLOBAL)
196dc0093f4Seschrock 			return (1);
197dc0093f4Seschrock 	}
198dc0093f4Seschrock 
199dc0093f4Seschrock 	/*
200dc0093f4Seschrock 	 * As a last resort, if we have multiple symbols of the same type at the
201dc0093f4Seschrock 	 * same address, prefer the version with the fewest leading underscores.
202dc0093f4Seschrock 	 */
203dc0093f4Seschrock 	if (aname == NULL)
204dc0093f4Seschrock 		return (-1);
205dc0093f4Seschrock 	if (bname == NULL)
206dc0093f4Seschrock 		return (1);
207dc0093f4Seschrock 
208dc0093f4Seschrock 	while (*aname == '_' && *bname == '_') {
209dc0093f4Seschrock 		aname++;
210dc0093f4Seschrock 		bname++;
211dc0093f4Seschrock 	}
212dc0093f4Seschrock 
213dc0093f4Seschrock 	if (*bname == '_')
214dc0093f4Seschrock 		return (-1);
215dc0093f4Seschrock 	if (*aname == '_')
216dc0093f4Seschrock 		return (1);
217dc0093f4Seschrock 
218dc0093f4Seschrock 	/*
219dc0093f4Seschrock 	 * Prefer the symbol with the smaller size.
220dc0093f4Seschrock 	 */
221dc0093f4Seschrock 	if (syma->se_sym.st_size < symb->se_sym.st_size)
222dc0093f4Seschrock 		return (-1);
223dc0093f4Seschrock 	if (syma->se_sym.st_size > symb->se_sym.st_size)
224dc0093f4Seschrock 		return (1);
225dc0093f4Seschrock 
226dc0093f4Seschrock 	/*
227dc0093f4Seschrock 	 * We really do have two identical symbols for some reason.  Just report
228dc0093f4Seschrock 	 * them as equal, and to the lucky one go the spoils.
229dc0093f4Seschrock 	 */
230dc0093f4Seschrock 	return (0);
231dc0093f4Seschrock }
232dc0093f4Seschrock 
233dc0093f4Seschrock /*
234dc0093f4Seschrock  * Construct an optimized symbol table sorted by starting address.
235dc0093f4Seschrock  */
236dc0093f4Seschrock static void
237dc0093f4Seschrock construct_symtab(dis_tgt_t *tgt)
238dc0093f4Seschrock {
239dc0093f4Seschrock 	Elf_Scn *scn;
240dc0093f4Seschrock 	GElf_Shdr shdr;
241dc0093f4Seschrock 	Elf_Data *symdata;
242dc0093f4Seschrock 	int i;
243dc0093f4Seschrock 	GElf_Word *symshndx = NULL;
244dc0093f4Seschrock 	int symshndx_size;
245dc0093f4Seschrock 	sym_entry_t *sym;
246dc0093f4Seschrock 	sym_entry_t *p_symtab = NULL;
247dc0093f4Seschrock 	int nsym = 0; /* count of symbols we're not interested in */
248dc0093f4Seschrock 
249dc0093f4Seschrock 	/*
250dc0093f4Seschrock 	 * Find the symshndx section, if any
251dc0093f4Seschrock 	 */
252dc0093f4Seschrock 	for (scn = elf_nextscn(tgt->dt_elf, NULL); scn != NULL;
253dc0093f4Seschrock 	    scn = elf_nextscn(tgt->dt_elf, scn)) {
254dc0093f4Seschrock 		if (gelf_getshdr(scn, &shdr) == NULL)
255dc0093f4Seschrock 			break;
256dc0093f4Seschrock 		if (shdr.sh_type == SHT_SYMTAB_SHNDX &&
257dc0093f4Seschrock 		    shdr.sh_link == tgt->dt_symidx) {
258dc0093f4Seschrock 			Elf_Data	*data;
259dc0093f4Seschrock 
260dc0093f4Seschrock 			if ((data = elf_getdata(scn, NULL)) != NULL) {
261dc0093f4Seschrock 				symshndx = (GElf_Word *)data->d_buf;
262dc0093f4Seschrock 				symshndx_size = data->d_size /
263dc0093f4Seschrock 				    sizeof (GElf_Word);
264dc0093f4Seschrock 				break;
265dc0093f4Seschrock 			}
266dc0093f4Seschrock 		}
267dc0093f4Seschrock 	}
268dc0093f4Seschrock 
269dc0093f4Seschrock 	if ((scn = elf_getscn(tgt->dt_elf, tgt->dt_symidx)) == NULL)
270dc0093f4Seschrock 		die("%s: failed to get section information", tgt->dt_filename);
271dc0093f4Seschrock 	if (gelf_getshdr(scn, &shdr) == NULL)
272dc0093f4Seschrock 		die("%s: failed to get section header", tgt->dt_filename);
273dc0093f4Seschrock 	if (shdr.sh_entsize == 0)
274dc0093f4Seschrock 		die("%s: symbol table has zero size", tgt->dt_filename);
275dc0093f4Seschrock 
276dc0093f4Seschrock 	if ((symdata = elf_getdata(scn, NULL)) == NULL)
277dc0093f4Seschrock 		die("%s: failed to get symbol table", tgt->dt_filename);
278dc0093f4Seschrock 
279dc0093f4Seschrock 	tgt->dt_symcount = symdata->d_size / gelf_fsize(tgt->dt_elf, ELF_T_SYM,
280dc0093f4Seschrock 	    1, EV_CURRENT);
281dc0093f4Seschrock 
282dc0093f4Seschrock 	p_symtab = safe_malloc(tgt->dt_symcount * sizeof (sym_entry_t));
283dc0093f4Seschrock 
284dc0093f4Seschrock 	for (i = 0, sym = p_symtab; i < tgt->dt_symcount; i++) {
285dc0093f4Seschrock 		if (gelf_getsym(symdata, i, &(sym->se_sym)) == NULL) {
286dc0093f4Seschrock 			warn("%s: gelf_getsym returned NULL for %d",
287dc0093f4Seschrock 			    tgt->dt_filename, i);
288dc0093f4Seschrock 			nsym++;
289dc0093f4Seschrock 			continue;
290dc0093f4Seschrock 		}
291dc0093f4Seschrock 
292dc0093f4Seschrock 		/*
293dc0093f4Seschrock 		 * We're only interested in data symbols.
294dc0093f4Seschrock 		 */
295dc0093f4Seschrock 		if (!IS_DATA_TYPE(GELF_ST_TYPE(sym->se_sym.st_info))) {
296dc0093f4Seschrock 			nsym++;
297dc0093f4Seschrock 			continue;
298dc0093f4Seschrock 		}
299dc0093f4Seschrock 
300dc0093f4Seschrock 		if (sym->se_sym.st_shndx == SHN_XINDEX && symshndx != NULL) {
301dc0093f4Seschrock 			if (i > symshndx_size) {
302dc0093f4Seschrock 				warn("%s: bad SHNX_XINDEX %d",
303dc0093f4Seschrock 				    tgt->dt_filename, i);
304dc0093f4Seschrock 				sym->se_shndx = -1;
305dc0093f4Seschrock 			} else {
306dc0093f4Seschrock 				sym->se_shndx = symshndx[i];
307dc0093f4Seschrock 			}
308dc0093f4Seschrock 		} else {
309dc0093f4Seschrock 			sym->se_shndx = sym->se_sym.st_shndx;
310dc0093f4Seschrock 		}
311dc0093f4Seschrock 
3126e6df3cfSJason King 		/* Deal with symbols with special section indicies */
3136e6df3cfSJason King 		if (sym->se_shndx == SHN_ABS) {
3146e6df3cfSJason King 			/*
3156e6df3cfSJason King 			 * If st_value == 0, references to these
3166e6df3cfSJason King 			 * symbols in code are modified in situ
3176e6df3cfSJason King 			 * thus we will never attempt to look
3186e6df3cfSJason King 			 * them up.
3196e6df3cfSJason King 			 */
3206e6df3cfSJason King 			if (sym->se_sym.st_value == 0) {
3216e6df3cfSJason King 				/*
3226e6df3cfSJason King 				 * References to these symbols in code
3236e6df3cfSJason King 				 * are modified in situ by the runtime
3246e6df3cfSJason King 				 * linker and no code on disk will ever
3256e6df3cfSJason King 				 * attempt to look them up.
3266e6df3cfSJason King 				 */
3276e6df3cfSJason King 				nsym++;
3286e6df3cfSJason King 				continue;
3296e6df3cfSJason King 			} else {
3306e6df3cfSJason King 				/*
3316e6df3cfSJason King 				 * If st_value != 0, (such as examining
3326e6df3cfSJason King 				 * something in /system/object/.../object)
3336e6df3cfSJason King 				 * the values should resolve to a value
3346e6df3cfSJason King 				 * within an existing section (such as
3356e6df3cfSJason King 				 * .data).  This also means it never needs
3366e6df3cfSJason King 				 * to have st_value mapped.
3376e6df3cfSJason King 				 */
3386e6df3cfSJason King 				sym++;
3396e6df3cfSJason King 				continue;
3406e6df3cfSJason King 			}
3416e6df3cfSJason King 		}
3426e6df3cfSJason King 
3436e6df3cfSJason King 		/*
3446e6df3cfSJason King 		 * Ignore the symbol if it has some other special
3456e6df3cfSJason King 		 * section index
3466e6df3cfSJason King 		 */
3476e6df3cfSJason King 		if (sym->se_shndx == SHN_UNDEF ||
3486e6df3cfSJason King 		    sym->se_shndx >= SHN_LORESERVE) {
3496e6df3cfSJason King 			nsym++;
3506e6df3cfSJason King 			continue;
3516e6df3cfSJason King 		}
3526e6df3cfSJason King 
353dc0093f4Seschrock 		if ((sym->se_name = elf_strptr(tgt->dt_elf, shdr.sh_link,
354dc0093f4Seschrock 		    (size_t)sym->se_sym.st_name)) == NULL) {
355dc0093f4Seschrock 			warn("%s: failed to lookup symbol %d name",
356dc0093f4Seschrock 			    tgt->dt_filename, i);
357dc0093f4Seschrock 			nsym++;
358dc0093f4Seschrock 			continue;
359dc0093f4Seschrock 		}
360dc0093f4Seschrock 
361b5f3c6ffSJason King 		/*
362b5f3c6ffSJason King 		 * If we had to map this section, its symbol value
363b5f3c6ffSJason King 		 * also needs to be mapped.
364b5f3c6ffSJason King 		 */
365b5f3c6ffSJason King 		if (tgt->dt_shnmap[sym->se_shndx].dm_mapped)
366b5f3c6ffSJason King 			sym->se_sym.st_value +=
367b5f3c6ffSJason King 			    tgt->dt_shnmap[sym->se_shndx].dm_start;
368b5f3c6ffSJason King 
369dc0093f4Seschrock 		sym++;
370dc0093f4Seschrock 	}
371dc0093f4Seschrock 
372dc0093f4Seschrock 	tgt->dt_symcount -= nsym;
3737a65609eSjmcp 	tgt->dt_symtab = realloc(p_symtab, tgt->dt_symcount *
3747a65609eSjmcp 	    sizeof (sym_entry_t));
375dc0093f4Seschrock 
376dc0093f4Seschrock 	qsort(tgt->dt_symtab, tgt->dt_symcount, sizeof (sym_entry_t),
377dc0093f4Seschrock 	    sym_compare);
378dc0093f4Seschrock }
379dc0093f4Seschrock 
380dc0093f4Seschrock /*
381b5f3c6ffSJason King  * Assign virtual address ranges for sections that need it
382b5f3c6ffSJason King  */
383b5f3c6ffSJason King static void
384b5f3c6ffSJason King create_addrmap(dis_tgt_t *tgt)
385b5f3c6ffSJason King {
386b5f3c6ffSJason King 	uint64_t addr;
387b5f3c6ffSJason King 	int i;
388b5f3c6ffSJason King 
389b5f3c6ffSJason King 	if (tgt->dt_shnmap == NULL)
390b5f3c6ffSJason King 		return;
391b5f3c6ffSJason King 
392b5f3c6ffSJason King 	/* find the greatest used address */
393b5f3c6ffSJason King 	for (addr = 0, i = 1; i < tgt->dt_shncount; i++)
394b5f3c6ffSJason King 		if (tgt->dt_shnmap[i].dm_start > addr)
395b5f3c6ffSJason King 			addr = tgt->dt_shnmap[i].dm_start +
396b5f3c6ffSJason King 			    tgt->dt_shnmap[i].dm_length;
397b5f3c6ffSJason King 
398b5f3c6ffSJason King 	addr = P2ROUNDUP(addr, 0x1000);
399b5f3c6ffSJason King 
400b5f3c6ffSJason King 	/*
401b5f3c6ffSJason King 	 * Assign section a starting address beyond the largest mapped section
402b5f3c6ffSJason King 	 * if no address was given.
403b5f3c6ffSJason King 	 */
404b5f3c6ffSJason King 	for (i = 1; i < tgt->dt_shncount; i++) {
405b5f3c6ffSJason King 		if (tgt->dt_shnmap[i].dm_start != 0)
406b5f3c6ffSJason King 			continue;
407b5f3c6ffSJason King 
408b5f3c6ffSJason King 		tgt->dt_shnmap[i].dm_start = addr;
409b5f3c6ffSJason King 		tgt->dt_shnmap[i].dm_mapped = B_TRUE;
410b5f3c6ffSJason King 		addr = P2ROUNDUP(addr + tgt->dt_shnmap[i].dm_length, 0x1000);
411b5f3c6ffSJason King 	}
412b5f3c6ffSJason King }
413b5f3c6ffSJason King 
414b5f3c6ffSJason King /*
415dc0093f4Seschrock  * Create a target backed by an ELF file.
416dc0093f4Seschrock  */
417dc0093f4Seschrock dis_tgt_t *
418dc0093f4Seschrock dis_tgt_create(const char *file)
419dc0093f4Seschrock {
420dc0093f4Seschrock 	dis_tgt_t *tgt, *current;
421dc0093f4Seschrock 	int idx;
422dc0093f4Seschrock 	Elf *elf;
423dc0093f4Seschrock 	GElf_Ehdr ehdr;
424dc0093f4Seschrock 	Elf_Arhdr *arhdr = NULL;
425dc0093f4Seschrock 	int cmd;
426dc0093f4Seschrock 
427dc0093f4Seschrock 	if (elf_version(EV_CURRENT) == EV_NONE)
428dc0093f4Seschrock 		die("libelf(3ELF) out of date");
429dc0093f4Seschrock 
430dc0093f4Seschrock 	tgt = safe_malloc(sizeof (dis_tgt_t));
431dc0093f4Seschrock 
432dc0093f4Seschrock 	if ((tgt->dt_fd = open(file, O_RDONLY)) < 0) {
433dc0093f4Seschrock 		warn("%s: failed opening file, reason: %s", file,
434dc0093f4Seschrock 		    strerror(errno));
435dc0093f4Seschrock 		free(tgt);
436dc0093f4Seschrock 		return (NULL);
437dc0093f4Seschrock 	}
438dc0093f4Seschrock 
439dc0093f4Seschrock 	if ((tgt->dt_elf_root =
440dc0093f4Seschrock 	    elf_begin(tgt->dt_fd, ELF_C_READ, NULL)) == NULL) {
441dc0093f4Seschrock 		warn("%s: invalid or corrupt ELF file", file);
442dc0093f4Seschrock 		dis_tgt_destroy(tgt);
443dc0093f4Seschrock 		return (NULL);
444dc0093f4Seschrock 	}
445dc0093f4Seschrock 
446dc0093f4Seschrock 	current = tgt;
447dc0093f4Seschrock 	cmd = ELF_C_READ;
448dc0093f4Seschrock 	while ((elf = elf_begin(tgt->dt_fd, cmd, tgt->dt_elf_root)) != NULL) {
44927553b5cSRichard Lowe 		size_t shnum = 0;
450dc0093f4Seschrock 
451dc0093f4Seschrock 		if (elf_kind(tgt->dt_elf_root) == ELF_K_AR &&
452dc0093f4Seschrock 		    (arhdr = elf_getarhdr(elf)) == NULL) {
453dc0093f4Seschrock 			warn("%s: malformed archive", file);
454dc0093f4Seschrock 			dis_tgt_destroy(tgt);
455dc0093f4Seschrock 			return (NULL);
456dc0093f4Seschrock 		}
457dc0093f4Seschrock 
458dc0093f4Seschrock 		/*
459dc0093f4Seschrock 		 * Make sure that this Elf file is sane
460dc0093f4Seschrock 		 */
461dc0093f4Seschrock 		if (gelf_getehdr(elf, &ehdr) == NULL) {
462dc0093f4Seschrock 			if (arhdr != NULL) {
463dc0093f4Seschrock 				/*
464dc0093f4Seschrock 				 * For archives, we drive on in the face of bad
465dc0093f4Seschrock 				 * members.  The "/" and "//" members are
466dc0093f4Seschrock 				 * special, and should be silently ignored.
467dc0093f4Seschrock 				 */
468dc0093f4Seschrock 				if (strcmp(arhdr->ar_name, "/") != 0 &&
469dc0093f4Seschrock 				    strcmp(arhdr->ar_name, "//") != 0)
470dc0093f4Seschrock 					warn("%s[%s]: invalid file type",
471dc0093f4Seschrock 					    file, arhdr->ar_name);
472dc0093f4Seschrock 				cmd = elf_next(elf);
473dc0093f4Seschrock 				(void) elf_end(elf);
474dc0093f4Seschrock 				continue;
475dc0093f4Seschrock 			}
476dc0093f4Seschrock 
477dc0093f4Seschrock 			warn("%s: invalid file type", file);
478dc0093f4Seschrock 			dis_tgt_destroy(tgt);
479dc0093f4Seschrock 			return (NULL);
480dc0093f4Seschrock 		}
481dc0093f4Seschrock 
482dc0093f4Seschrock 		/*
483dc0093f4Seschrock 		 * If we're seeing a new Elf object, then we have an
484dc0093f4Seschrock 		 * archive. In this case, we create a new target, and chain it
485dc0093f4Seschrock 		 * off the master target.  We can later iterate over these
486dc0093f4Seschrock 		 * targets using dis_tgt_next().
487dc0093f4Seschrock 		 */
488dc0093f4Seschrock 		if (current->dt_elf != NULL) {
489dc0093f4Seschrock 			dis_tgt_t *next = safe_malloc(sizeof (dis_tgt_t));
490dc0093f4Seschrock 			next->dt_elf_root = tgt->dt_elf_root;
491dc0093f4Seschrock 			next->dt_fd = -1;
492dc0093f4Seschrock 			current->dt_next = next;
493dc0093f4Seschrock 			current = next;
494dc0093f4Seschrock 		}
495dc0093f4Seschrock 		current->dt_elf = elf;
496dc0093f4Seschrock 		current->dt_arhdr = arhdr;
497dc0093f4Seschrock 
49862b628a6SAli Bahrami 		if (elf_getshdrstrndx(elf, &current->dt_shstrndx) == -1) {
499dc0093f4Seschrock 			warn("%s: failed to get section string table for "
500dc0093f4Seschrock 			    "file", file);
501dc0093f4Seschrock 			dis_tgt_destroy(tgt);
502dc0093f4Seschrock 			return (NULL);
503dc0093f4Seschrock 		}
504dc0093f4Seschrock 
50527553b5cSRichard Lowe 		if (elf_getshdrnum(elf, &shnum) == -1) {
50627553b5cSRichard Lowe 			warn("%s: failed to get number of sections in file",
50727553b5cSRichard Lowe 			    file);
50827553b5cSRichard Lowe 			dis_tgt_destroy(tgt);
50927553b5cSRichard Lowe 			return (NULL);
51027553b5cSRichard Lowe 		}
51127553b5cSRichard Lowe 
512b5f3c6ffSJason King 		current->dt_shnmap = safe_malloc(sizeof (dis_shnmap_t) *
51327553b5cSRichard Lowe 		    shnum);
51427553b5cSRichard Lowe 		current->dt_shncount = shnum;
515dc0093f4Seschrock 
516b5f3c6ffSJason King 		idx = 0;
517b5f3c6ffSJason King 		dis_tgt_section_iter(current, tgt_scn_init, &idx);
5186e6df3cfSJason King 		current->dt_filename = file;
519b5f3c6ffSJason King 
520b5f3c6ffSJason King 		create_addrmap(current);
521dc0093f4Seschrock 		if (current->dt_symidx != 0)
522dc0093f4Seschrock 			construct_symtab(current);
523dc0093f4Seschrock 
524dc0093f4Seschrock 		cmd = elf_next(elf);
525dc0093f4Seschrock 	}
526dc0093f4Seschrock 
527dc0093f4Seschrock 	/*
528dc0093f4Seschrock 	 * Final sanity check.  If we had an archive with no members, then bail
529dc0093f4Seschrock 	 * out with a nice message.
530dc0093f4Seschrock 	 */
531dc0093f4Seschrock 	if (tgt->dt_elf == NULL) {
532dc0093f4Seschrock 		warn("%s: empty archive\n", file);
533dc0093f4Seschrock 		dis_tgt_destroy(tgt);
534dc0093f4Seschrock 		return (NULL);
535dc0093f4Seschrock 	}
536dc0093f4Seschrock 
537dc0093f4Seschrock 	return (tgt);
538dc0093f4Seschrock }
539dc0093f4Seschrock 
540dc0093f4Seschrock /*
541dc0093f4Seschrock  * Return the filename associated with the target.
542dc0093f4Seschrock  */
543dc0093f4Seschrock const char *
544dc0093f4Seschrock dis_tgt_name(dis_tgt_t *tgt)
545dc0093f4Seschrock {
546dc0093f4Seschrock 	return (tgt->dt_filename);
547dc0093f4Seschrock }
548dc0093f4Seschrock 
549dc0093f4Seschrock /*
550dc0093f4Seschrock  * Return the archive member name, if any.
551dc0093f4Seschrock  */
552dc0093f4Seschrock const char *
553dc0093f4Seschrock dis_tgt_member(dis_tgt_t *tgt)
554dc0093f4Seschrock {
555dc0093f4Seschrock 	if (tgt->dt_arhdr)
556dc0093f4Seschrock 		return (tgt->dt_arhdr->ar_name);
557dc0093f4Seschrock 	else
558dc0093f4Seschrock 		return (NULL);
559dc0093f4Seschrock }
560dc0093f4Seschrock 
561dc0093f4Seschrock /*
562dc0093f4Seschrock  * Return the Elf_Ehdr associated with this target.  Needed to determine which
563dc0093f4Seschrock  * disassembler to use.
564dc0093f4Seschrock  */
565dc0093f4Seschrock void
566dc0093f4Seschrock dis_tgt_ehdr(dis_tgt_t *tgt, GElf_Ehdr *ehdr)
567dc0093f4Seschrock {
568dc0093f4Seschrock 	(void) gelf_getehdr(tgt->dt_elf, ehdr);
569dc0093f4Seschrock }
570dc0093f4Seschrock 
571dc0093f4Seschrock /*
572dc0093f4Seschrock  * Return the next target in the list, if this is an archive.
573dc0093f4Seschrock  */
574dc0093f4Seschrock dis_tgt_t *
575dc0093f4Seschrock dis_tgt_next(dis_tgt_t *tgt)
576dc0093f4Seschrock {
577dc0093f4Seschrock 	return (tgt->dt_next);
578dc0093f4Seschrock }
579dc0093f4Seschrock 
580dc0093f4Seschrock /*
581dc0093f4Seschrock  * Destroy a target and free up any associated memory.
582dc0093f4Seschrock  */
583dc0093f4Seschrock void
584dc0093f4Seschrock dis_tgt_destroy(dis_tgt_t *tgt)
585dc0093f4Seschrock {
586dc0093f4Seschrock 	dis_tgt_t *current, *next;
587dc0093f4Seschrock 
588dc0093f4Seschrock 	current = tgt->dt_next;
589dc0093f4Seschrock 	while (current != NULL) {
590dc0093f4Seschrock 		next = current->dt_next;
591dc0093f4Seschrock 		if (current->dt_elf)
592dc0093f4Seschrock 			(void) elf_end(current->dt_elf);
593dc0093f4Seschrock 		if (current->dt_symtab)
594dc0093f4Seschrock 			free(current->dt_symtab);
595dc0093f4Seschrock 		free(current);
596dc0093f4Seschrock 		current = next;
597dc0093f4Seschrock 	}
598dc0093f4Seschrock 
599dc0093f4Seschrock 	if (tgt->dt_elf)
600dc0093f4Seschrock 		(void) elf_end(tgt->dt_elf);
601dc0093f4Seschrock 	if (tgt->dt_elf_root)
602dc0093f4Seschrock 		(void) elf_end(tgt->dt_elf_root);
603dc0093f4Seschrock 
604dc0093f4Seschrock 	if (tgt->dt_symtab)
605dc0093f4Seschrock 		free(tgt->dt_symtab);
606dc0093f4Seschrock 
607dc0093f4Seschrock 	free(tgt);
608dc0093f4Seschrock }
609dc0093f4Seschrock 
610dc0093f4Seschrock /*
611b5f3c6ffSJason King  * Given an address, return the section it is in and set the offset within
612b5f3c6ffSJason King  * the section.
613b5f3c6ffSJason King  */
614b5f3c6ffSJason King const char *
615b5f3c6ffSJason King dis_find_section(dis_tgt_t *tgt, uint64_t addr, off_t *offset)
616b5f3c6ffSJason King {
617b5f3c6ffSJason King 	int i;
618b5f3c6ffSJason King 
619b5f3c6ffSJason King 	for (i = 1; i < tgt->dt_shncount; i++) {
620b5f3c6ffSJason King 		if ((addr >= tgt->dt_shnmap[i].dm_start) &&
621b5f3c6ffSJason King 		    (addr < tgt->dt_shnmap[i].dm_start +
622b5f3c6ffSJason King 		    tgt->dt_shnmap[i].dm_length)) {
623b5f3c6ffSJason King 			*offset = addr - tgt->dt_shnmap[i].dm_start;
624b5f3c6ffSJason King 			return (tgt->dt_shnmap[i].dm_name);
625b5f3c6ffSJason King 		}
626b5f3c6ffSJason King 	}
627b5f3c6ffSJason King 
628b5f3c6ffSJason King 	*offset = 0;
629b5f3c6ffSJason King 	return (NULL);
630b5f3c6ffSJason King }
631b5f3c6ffSJason King 
632b5f3c6ffSJason King /*
633dc0093f4Seschrock  * Given an address, returns the name of the corresponding symbol, as well as
634dc0093f4Seschrock  * the offset within that symbol.  If no matching symbol is found, then NULL is
635dc0093f4Seschrock  * returned.
636dc0093f4Seschrock  *
637dc0093f4Seschrock  * If 'cache_result' is specified, then we keep track of the resulting symbol.
638dc0093f4Seschrock  * This cached result is consulted first on subsequent lookups in order to avoid
639dc0093f4Seschrock  * unecessary lookups.  This flag should be used for resolving the current PC,
640dc0093f4Seschrock  * as the majority of addresses stay within the current function.
641dc0093f4Seschrock  */
642dc0093f4Seschrock const char *
643dc0093f4Seschrock dis_tgt_lookup(dis_tgt_t *tgt, uint64_t addr, off_t *offset, int cache_result,
644dc0093f4Seschrock     size_t *size, int *isfunc)
645dc0093f4Seschrock {
646dc0093f4Seschrock 	int lo, hi, mid;
647dc0093f4Seschrock 	sym_entry_t *sym, *osym, *match;
648dc0093f4Seschrock 	int found;
649dc0093f4Seschrock 
650*dc986d9fSRichard Lowe 	*offset = 0;
651*dc986d9fSRichard Lowe 	*size = 0;
652*dc986d9fSRichard Lowe 	if (isfunc != NULL)
653*dc986d9fSRichard Lowe 		*isfunc = 0;
654*dc986d9fSRichard Lowe 
655dc0093f4Seschrock 	if (tgt->dt_symcache != NULL &&
656dc0093f4Seschrock 	    addr >= tgt->dt_symcache->se_sym.st_value &&
657dc0093f4Seschrock 	    addr < tgt->dt_symcache->se_sym.st_value +
658dc0093f4Seschrock 	    tgt->dt_symcache->se_sym.st_size) {
659*dc986d9fSRichard Lowe 		sym = tgt->dt_symcache;
660*dc986d9fSRichard Lowe 		*offset = addr - sym->se_sym.st_value;
661*dc986d9fSRichard Lowe 		*size = sym->se_sym.st_size;
662*dc986d9fSRichard Lowe 		if (isfunc != NULL)
663*dc986d9fSRichard Lowe 			*isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) ==
664*dc986d9fSRichard Lowe 			    STT_FUNC);
665*dc986d9fSRichard Lowe 		return (sym->se_name);
666dc0093f4Seschrock 	}
667dc0093f4Seschrock 
668dc0093f4Seschrock 	lo = 0;
669dc0093f4Seschrock 	hi = (tgt->dt_symcount - 1);
670dc0093f4Seschrock 	found = 0;
671dc0093f4Seschrock 	match = osym = NULL;
672dc0093f4Seschrock 	while (lo <= hi) {
673dc0093f4Seschrock 		mid = (lo + hi) / 2;
674dc0093f4Seschrock 
675dc0093f4Seschrock 		sym = &tgt->dt_symtab[mid];
676dc0093f4Seschrock 
677dc0093f4Seschrock 		if (addr >= sym->se_sym.st_value &&
678dc0093f4Seschrock 		    addr < sym->se_sym.st_value + sym->se_sym.st_size &&
679dc0093f4Seschrock 		    (!found || sym->se_sym.st_value > osym->se_sym.st_value)) {
680dc0093f4Seschrock 			osym = sym;
681dc0093f4Seschrock 			found = 1;
682dc0093f4Seschrock 		} else if (addr == sym->se_sym.st_value) {
683dc0093f4Seschrock 			/*
684dc0093f4Seschrock 			 * Particularly for .plt objects, it's possible to have
685dc0093f4Seschrock 			 * a zero sized object.  We want to return this, but we
686dc0093f4Seschrock 			 * want it to be a last resort.
687dc0093f4Seschrock 			 */
688dc0093f4Seschrock 			match = sym;
689dc0093f4Seschrock 		}
690dc0093f4Seschrock 
691dc0093f4Seschrock 		if (addr < sym->se_sym.st_value)
692dc0093f4Seschrock 			hi = mid - 1;
693dc0093f4Seschrock 		else
694dc0093f4Seschrock 			lo = mid + 1;
695dc0093f4Seschrock 	}
696dc0093f4Seschrock 
697dc0093f4Seschrock 	if (!found) {
698dc0093f4Seschrock 		if (match)
699dc0093f4Seschrock 			osym = match;
700dc0093f4Seschrock 		else
701dc0093f4Seschrock 			return (NULL);
702dc0093f4Seschrock 	}
703dc0093f4Seschrock 
704dc0093f4Seschrock 	/*
705dc0093f4Seschrock 	 * Walk backwards to find the best match.
706dc0093f4Seschrock 	 */
707dc0093f4Seschrock 	do {
708dc0093f4Seschrock 		sym = osym;
709dc0093f4Seschrock 
710dc0093f4Seschrock 		if (osym == tgt->dt_symtab)
711dc0093f4Seschrock 			break;
712dc0093f4Seschrock 
713dc0093f4Seschrock 		osym = osym - 1;
714dc0093f4Seschrock 	} while ((sym->se_sym.st_value == osym->se_sym.st_value) &&
715dc0093f4Seschrock 	    (addr >= osym->se_sym.st_value) &&
716dc0093f4Seschrock 	    (addr < osym->se_sym.st_value + osym->se_sym.st_size));
717dc0093f4Seschrock 
718dc0093f4Seschrock 	if (cache_result)
719dc0093f4Seschrock 		tgt->dt_symcache = sym;
720dc0093f4Seschrock 
721dc0093f4Seschrock 	*offset = addr - sym->se_sym.st_value;
722dc0093f4Seschrock 	*size = sym->se_sym.st_size;
723dc0093f4Seschrock 	if (isfunc)
724dc0093f4Seschrock 		*isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) == STT_FUNC);
725dc0093f4Seschrock 
726dc0093f4Seschrock 	return (sym->se_name);
727dc0093f4Seschrock }
728dc0093f4Seschrock 
7296e6df3cfSJason King #if !defined(__sparc)
730dc0093f4Seschrock /*
731dc0093f4Seschrock  * Given an address, return the starting offset of the next symbol in the file.
7326e6df3cfSJason King  * Only needed on variable length instruction architectures.
733dc0093f4Seschrock  */
734dc0093f4Seschrock off_t
735dc0093f4Seschrock dis_tgt_next_symbol(dis_tgt_t *tgt, uint64_t addr)
736dc0093f4Seschrock {
737b5f3c6ffSJason King 	sym_entry_t *sym;
738dc0093f4Seschrock 
739edd4ab01SRichard Lowe 	sym = (tgt->dt_symcache != NULL) ? tgt->dt_symcache : tgt->dt_symtab;
740edd4ab01SRichard Lowe 
741edd4ab01SRichard Lowe 	while (sym != (tgt->dt_symtab + tgt->dt_symcount)) {
742b5f3c6ffSJason King 		if (sym->se_sym.st_value >= addr)
743dc0093f4Seschrock 			return (sym->se_sym.st_value - addr);
744edd4ab01SRichard Lowe 		sym++;
745dc0093f4Seschrock 	}
746dc0093f4Seschrock 
747b5f3c6ffSJason King 	return (0);
748b5f3c6ffSJason King }
7496e6df3cfSJason King #endif
750b5f3c6ffSJason King 
751dc0093f4Seschrock /*
752dc0093f4Seschrock  * Iterate over all sections in the target, executing the given callback for
753dc0093f4Seschrock  * each.
754dc0093f4Seschrock  */
755dc0093f4Seschrock void
756dc0093f4Seschrock dis_tgt_section_iter(dis_tgt_t *tgt, section_iter_f func, void *data)
757dc0093f4Seschrock {
758dc0093f4Seschrock 	dis_scn_t sdata;
759dc0093f4Seschrock 	Elf_Scn *scn;
760dc0093f4Seschrock 	int idx;
761dc0093f4Seschrock 
762dc0093f4Seschrock 	for (scn = elf_nextscn(tgt->dt_elf, NULL), idx = 1; scn != NULL;
763dc0093f4Seschrock 	    scn = elf_nextscn(tgt->dt_elf, scn), idx++) {
764dc0093f4Seschrock 
765dc0093f4Seschrock 		if (gelf_getshdr(scn, &sdata.ds_shdr) == NULL) {
766dc0093f4Seschrock 			warn("%s: failed to get section %d header",
767dc0093f4Seschrock 			    tgt->dt_filename, idx);
768dc0093f4Seschrock 			continue;
769dc0093f4Seschrock 		}
770dc0093f4Seschrock 
771dc0093f4Seschrock 		if ((sdata.ds_name = elf_strptr(tgt->dt_elf, tgt->dt_shstrndx,
772dc0093f4Seschrock 		    sdata.ds_shdr.sh_name)) == NULL) {
773dc0093f4Seschrock 			warn("%s: failed to get section %d name",
774dc0093f4Seschrock 			    tgt->dt_filename, idx);
775dc0093f4Seschrock 			continue;
776dc0093f4Seschrock 		}
777dc0093f4Seschrock 
778dc0093f4Seschrock 		if ((sdata.ds_data = elf_getdata(scn, NULL)) == NULL) {
779dc0093f4Seschrock 			warn("%s: failed to get data for section '%s'",
780dc0093f4Seschrock 			    tgt->dt_filename, sdata.ds_name);
781dc0093f4Seschrock 			continue;
782dc0093f4Seschrock 		}
783dc0093f4Seschrock 
784b5f3c6ffSJason King 		/*
785b5f3c6ffSJason King 		 * dis_tgt_section_iter is also used before the section map
786b5f3c6ffSJason King 		 * is initialized, so only check when we need to.  If the
787b5f3c6ffSJason King 		 * section map is uninitialized, it will return 0 and have
788b5f3c6ffSJason King 		 * no net effect.
789b5f3c6ffSJason King 		 */
790b5f3c6ffSJason King 		if (sdata.ds_shdr.sh_addr == 0)
791b5f3c6ffSJason King 			sdata.ds_shdr.sh_addr = tgt->dt_shnmap[idx].dm_start;
792b5f3c6ffSJason King 
793dc0093f4Seschrock 		func(tgt, &sdata, data);
794dc0093f4Seschrock 	}
795dc0093f4Seschrock }
796dc0093f4Seschrock 
797dc0093f4Seschrock /*
798dc0093f4Seschrock  * Return 1 if the given section contains text, 0 otherwise.
799dc0093f4Seschrock  */
800dc0093f4Seschrock int
801dc0093f4Seschrock dis_section_istext(dis_scn_t *scn)
802dc0093f4Seschrock {
803dc0093f4Seschrock 	return ((scn->ds_shdr.sh_type == SHT_PROGBITS) &&
804dc0093f4Seschrock 	    (scn->ds_shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR)));
805dc0093f4Seschrock }
806dc0093f4Seschrock 
807dc0093f4Seschrock /*
808dc0093f4Seschrock  * Return a pointer to the section data.
809dc0093f4Seschrock  */
810dc0093f4Seschrock void *
811dc0093f4Seschrock dis_section_data(dis_scn_t *scn)
812dc0093f4Seschrock {
813dc0093f4Seschrock 	return (scn->ds_data->d_buf);
814dc0093f4Seschrock }
815dc0093f4Seschrock 
816dc0093f4Seschrock /*
817dc0093f4Seschrock  * Return the size of the section data.
818dc0093f4Seschrock  */
819dc0093f4Seschrock size_t
820dc0093f4Seschrock dis_section_size(dis_scn_t *scn)
821dc0093f4Seschrock {
822dc0093f4Seschrock 	return (scn->ds_data->d_size);
823dc0093f4Seschrock }
824dc0093f4Seschrock 
825dc0093f4Seschrock /*
826dc0093f4Seschrock  * Return the address for the given section.
827dc0093f4Seschrock  */
828dc0093f4Seschrock uint64_t
829dc0093f4Seschrock dis_section_addr(dis_scn_t *scn)
830dc0093f4Seschrock {
831dc0093f4Seschrock 	return (scn->ds_shdr.sh_addr);
832dc0093f4Seschrock }
833dc0093f4Seschrock 
834dc0093f4Seschrock /*
835dc0093f4Seschrock  * Return the name of the current section.
836dc0093f4Seschrock  */
837dc0093f4Seschrock const char *
838dc0093f4Seschrock dis_section_name(dis_scn_t *scn)
839dc0093f4Seschrock {
840dc0093f4Seschrock 	return (scn->ds_name);
841dc0093f4Seschrock }
842dc0093f4Seschrock 
843dc0093f4Seschrock /*
844dc0093f4Seschrock  * Create an allocated copy of the given section
845dc0093f4Seschrock  */
846dc0093f4Seschrock dis_scn_t *
847dc0093f4Seschrock dis_section_copy(dis_scn_t *scn)
848dc0093f4Seschrock {
849dc0093f4Seschrock 	dis_scn_t *new;
850dc0093f4Seschrock 
851dc0093f4Seschrock 	new = safe_malloc(sizeof (dis_scn_t));
852dc0093f4Seschrock 	(void) memcpy(new, scn, sizeof (dis_scn_t));
853dc0093f4Seschrock 
854dc0093f4Seschrock 	return (new);
855dc0093f4Seschrock }
856dc0093f4Seschrock 
857dc0093f4Seschrock /*
858dc0093f4Seschrock  * Free section memory
859dc0093f4Seschrock  */
860dc0093f4Seschrock void
861dc0093f4Seschrock dis_section_free(dis_scn_t *scn)
862dc0093f4Seschrock {
863dc0093f4Seschrock 	free(scn);
864dc0093f4Seschrock }
865dc0093f4Seschrock 
866dc0093f4Seschrock /*
867dc0093f4Seschrock  * Iterate over all functions in the target, executing the given callback for
868dc0093f4Seschrock  * each one.
869dc0093f4Seschrock  */
870dc0093f4Seschrock void
871dc0093f4Seschrock dis_tgt_function_iter(dis_tgt_t *tgt, function_iter_f func, void *data)
872dc0093f4Seschrock {
873dc0093f4Seschrock 	int i;
874dc0093f4Seschrock 	sym_entry_t *sym;
875dc0093f4Seschrock 	dis_func_t df;
876dc0093f4Seschrock 	Elf_Scn *scn;
877dc0093f4Seschrock 	GElf_Shdr	shdr;
878dc0093f4Seschrock 
879dc0093f4Seschrock 	for (i = 0, sym = tgt->dt_symtab; i < tgt->dt_symcount; i++, sym++) {
880dc0093f4Seschrock 
881dc0093f4Seschrock 		/* ignore non-functions */
882dc0093f4Seschrock 		if ((GELF_ST_TYPE(sym->se_sym.st_info) != STT_FUNC) ||
883161d9479Srie 		    (sym->se_name == NULL) ||
884161d9479Srie 		    (sym->se_sym.st_size == 0) ||
885161d9479Srie 		    (sym->se_shndx >= SHN_LORESERVE))
886dc0093f4Seschrock 			continue;
887dc0093f4Seschrock 
888dc0093f4Seschrock 		/* get the ELF data associated with this function */
889dc0093f4Seschrock 		if ((scn = elf_getscn(tgt->dt_elf, sym->se_shndx)) == NULL ||
890dc0093f4Seschrock 		    gelf_getshdr(scn, &shdr) == NULL ||
891dc0093f4Seschrock 		    (df.df_data = elf_getdata(scn, NULL)) == NULL ||
892dc0093f4Seschrock 		    df.df_data->d_size == 0) {
893dc0093f4Seschrock 			warn("%s: failed to read section %d",
894dc0093f4Seschrock 			    tgt->dt_filename, sym->se_shndx);
895dc0093f4Seschrock 			continue;
896dc0093f4Seschrock 		}
897dc0093f4Seschrock 
898b5f3c6ffSJason King 		if (tgt->dt_shnmap[sym->se_shndx].dm_mapped)
899b5f3c6ffSJason King 			shdr.sh_addr = tgt->dt_shnmap[sym->se_shndx].dm_start;
900b5f3c6ffSJason King 
901dc0093f4Seschrock 		/*
902dc0093f4Seschrock 		 * Verify that the address lies within the section that we think
903dc0093f4Seschrock 		 * it does.
904dc0093f4Seschrock 		 */
905dc0093f4Seschrock 		if (sym->se_sym.st_value < shdr.sh_addr ||
906dc0093f4Seschrock 		    (sym->se_sym.st_value + sym->se_sym.st_size) >
907dc0093f4Seschrock 		    (shdr.sh_addr + shdr.sh_size)) {
908dc0093f4Seschrock 			warn("%s: bad section %d for address %p",
909dc0093f4Seschrock 			    tgt->dt_filename, sym->se_sym.st_shndx,
910dc0093f4Seschrock 			    sym->se_sym.st_value);
911dc0093f4Seschrock 			continue;
912dc0093f4Seschrock 		}
913dc0093f4Seschrock 
914dc0093f4Seschrock 		df.df_sym = sym;
915dc0093f4Seschrock 		df.df_offset = sym->se_sym.st_value - shdr.sh_addr;
916dc0093f4Seschrock 
917dc0093f4Seschrock 		func(tgt, &df, data);
918dc0093f4Seschrock 	}
919dc0093f4Seschrock }
920dc0093f4Seschrock 
921dc0093f4Seschrock /*
922dc0093f4Seschrock  * Return the data associated with a given function.
923dc0093f4Seschrock  */
924dc0093f4Seschrock void *
925dc0093f4Seschrock dis_function_data(dis_func_t *func)
926dc0093f4Seschrock {
927dc0093f4Seschrock 	return ((char *)func->df_data->d_buf + func->df_offset);
928dc0093f4Seschrock }
929dc0093f4Seschrock 
930dc0093f4Seschrock /*
931dc0093f4Seschrock  * Return the size of a function.
932dc0093f4Seschrock  */
933dc0093f4Seschrock size_t
934dc0093f4Seschrock dis_function_size(dis_func_t *func)
935dc0093f4Seschrock {
936dc0093f4Seschrock 	return (func->df_sym->se_sym.st_size);
937dc0093f4Seschrock }
938dc0093f4Seschrock 
939dc0093f4Seschrock /*
940dc0093f4Seschrock  * Return the address of a function.
941dc0093f4Seschrock  */
942dc0093f4Seschrock uint64_t
943dc0093f4Seschrock dis_function_addr(dis_func_t *func)
944dc0093f4Seschrock {
945dc0093f4Seschrock 	return (func->df_sym->se_sym.st_value);
946dc0093f4Seschrock }
947dc0093f4Seschrock 
948dc0093f4Seschrock /*
949dc0093f4Seschrock  * Return the name of the function
950dc0093f4Seschrock  */
951dc0093f4Seschrock const char *
952dc0093f4Seschrock dis_function_name(dis_func_t *func)
953dc0093f4Seschrock {
954dc0093f4Seschrock 	return (func->df_sym->se_name);
955dc0093f4Seschrock }
956dc0093f4Seschrock 
957dc0093f4Seschrock /*
958dc0093f4Seschrock  * Return a copy of a function.
959dc0093f4Seschrock  */
960dc0093f4Seschrock dis_func_t *
961dc0093f4Seschrock dis_function_copy(dis_func_t *func)
962dc0093f4Seschrock {
963dc0093f4Seschrock 	dis_func_t *new;
964dc0093f4Seschrock 
965dc0093f4Seschrock 	new = safe_malloc(sizeof (dis_func_t));
966dc0093f4Seschrock 	(void) memcpy(new, func, sizeof (dis_func_t));
967dc0093f4Seschrock 
968dc0093f4Seschrock 	return (new);
969dc0093f4Seschrock }
970dc0093f4Seschrock 
971dc0093f4Seschrock /*
972dc0093f4Seschrock  * Free function memory
973dc0093f4Seschrock  */
974dc0093f4Seschrock void
975dc0093f4Seschrock dis_function_free(dis_func_t *func)
976dc0093f4Seschrock {
977dc0093f4Seschrock 	free(func);
978dc0093f4Seschrock }
979