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
tgt_scn_init(dis_tgt_t * tgt,dis_scn_t * scn,void * data)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
sym_compare(const void * a,const void * b)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;
160*7781236bSRichard Lowe size_t alen;
161*7781236bSRichard Lowe size_t blen;
162dc0093f4Seschrock
163dc0093f4Seschrock if (syma->se_sym.st_value < symb->se_sym.st_value)
164dc0093f4Seschrock return (-1);
165dc0093f4Seschrock
166dc0093f4Seschrock if (syma->se_sym.st_value > symb->se_sym.st_value)
167dc0093f4Seschrock return (1);
168dc0093f4Seschrock
169dc0093f4Seschrock /*
170dc0093f4Seschrock * Prefer functions over non-functions
171dc0093f4Seschrock */
172dc0093f4Seschrock if (GELF_ST_TYPE(syma->se_sym.st_info) !=
173dc0093f4Seschrock GELF_ST_TYPE(symb->se_sym.st_info)) {
174dc0093f4Seschrock if (GELF_ST_TYPE(syma->se_sym.st_info) == STT_FUNC)
175dc0093f4Seschrock return (-1);
176dc0093f4Seschrock if (GELF_ST_TYPE(symb->se_sym.st_info) == STT_FUNC)
177dc0093f4Seschrock return (1);
178dc0093f4Seschrock }
179dc0093f4Seschrock
180dc0093f4Seschrock /*
181dc0093f4Seschrock * For symbols with the same address and type, we sort them according to
182dc0093f4Seschrock * a hierarchy:
183dc0093f4Seschrock *
184dc0093f4Seschrock * 1. weak symbols (common name)
185dc0093f4Seschrock * 2. global symbols (external name)
186dc0093f4Seschrock * 3. local symbols
187dc0093f4Seschrock */
188dc0093f4Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) !=
189dc0093f4Seschrock GELF_ST_BIND(symb->se_sym.st_info)) {
190dc0093f4Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) == STB_WEAK)
191dc0093f4Seschrock return (-1);
192dc0093f4Seschrock if (GELF_ST_BIND(symb->se_sym.st_info) == STB_WEAK)
193dc0093f4Seschrock return (1);
194dc0093f4Seschrock
195dc0093f4Seschrock if (GELF_ST_BIND(syma->se_sym.st_info) == STB_GLOBAL)
196dc0093f4Seschrock return (-1);
197dc0093f4Seschrock if (GELF_ST_BIND(symb->se_sym.st_info) == STB_GLOBAL)
198dc0093f4Seschrock return (1);
199dc0093f4Seschrock }
200dc0093f4Seschrock
201dc0093f4Seschrock /*
202dc0093f4Seschrock * As a last resort, if we have multiple symbols of the same type at the
203dc0093f4Seschrock * same address, prefer the version with the fewest leading underscores.
204dc0093f4Seschrock */
205dc0093f4Seschrock if (aname == NULL)
206dc0093f4Seschrock return (-1);
207dc0093f4Seschrock if (bname == NULL)
208dc0093f4Seschrock return (1);
209dc0093f4Seschrock
210dc0093f4Seschrock while (*aname == '_' && *bname == '_') {
211dc0093f4Seschrock aname++;
212dc0093f4Seschrock bname++;
213dc0093f4Seschrock }
214dc0093f4Seschrock
215dc0093f4Seschrock if (*bname == '_')
216dc0093f4Seschrock return (-1);
217dc0093f4Seschrock if (*aname == '_')
218dc0093f4Seschrock return (1);
219dc0093f4Seschrock
220dc0093f4Seschrock /*
221dc0093f4Seschrock * Prefer the symbol with the smaller size.
222dc0093f4Seschrock */
223dc0093f4Seschrock if (syma->se_sym.st_size < symb->se_sym.st_size)
224dc0093f4Seschrock return (-1);
225dc0093f4Seschrock if (syma->se_sym.st_size > symb->se_sym.st_size)
226dc0093f4Seschrock return (1);
227dc0093f4Seschrock
228dc0093f4Seschrock /*
229*7781236bSRichard Lowe * We really do have two identical symbols, choose the one with the
230*7781236bSRichard Lowe * shortest name if we can, heuristically taking it to be the most
231*7781236bSRichard Lowe * representative.
232dc0093f4Seschrock */
233*7781236bSRichard Lowe alen = strlen(syma->se_name);
234*7781236bSRichard Lowe blen = strlen(symb->se_name);
235*7781236bSRichard Lowe
236*7781236bSRichard Lowe if (alen < blen)
237*7781236bSRichard Lowe return (-1);
238*7781236bSRichard Lowe else if (alen > blen)
239*7781236bSRichard Lowe return (1);
240*7781236bSRichard Lowe
241*7781236bSRichard Lowe /*
242*7781236bSRichard Lowe * If all else fails, compare the names, so that we give a stable
243*7781236bSRichard Lowe * sort
244*7781236bSRichard Lowe */
245*7781236bSRichard Lowe return (strcmp(syma->se_name, symb->se_name));
246dc0093f4Seschrock }
247dc0093f4Seschrock
248dc0093f4Seschrock /*
249dc0093f4Seschrock * Construct an optimized symbol table sorted by starting address.
250dc0093f4Seschrock */
251dc0093f4Seschrock static void
construct_symtab(dis_tgt_t * tgt)252dc0093f4Seschrock construct_symtab(dis_tgt_t *tgt)
253dc0093f4Seschrock {
254dc0093f4Seschrock Elf_Scn *scn;
255dc0093f4Seschrock GElf_Shdr shdr;
256dc0093f4Seschrock Elf_Data *symdata;
257dc0093f4Seschrock int i;
258dc0093f4Seschrock GElf_Word *symshndx = NULL;
259dc0093f4Seschrock int symshndx_size;
260dc0093f4Seschrock sym_entry_t *sym;
261dc0093f4Seschrock sym_entry_t *p_symtab = NULL;
262dc0093f4Seschrock int nsym = 0; /* count of symbols we're not interested in */
263dc0093f4Seschrock
264dc0093f4Seschrock /*
265dc0093f4Seschrock * Find the symshndx section, if any
266dc0093f4Seschrock */
267dc0093f4Seschrock for (scn = elf_nextscn(tgt->dt_elf, NULL); scn != NULL;
268dc0093f4Seschrock scn = elf_nextscn(tgt->dt_elf, scn)) {
269dc0093f4Seschrock if (gelf_getshdr(scn, &shdr) == NULL)
270dc0093f4Seschrock break;
271dc0093f4Seschrock if (shdr.sh_type == SHT_SYMTAB_SHNDX &&
272dc0093f4Seschrock shdr.sh_link == tgt->dt_symidx) {
273dc0093f4Seschrock Elf_Data *data;
274dc0093f4Seschrock
275dc0093f4Seschrock if ((data = elf_getdata(scn, NULL)) != NULL) {
276dc0093f4Seschrock symshndx = (GElf_Word *)data->d_buf;
277dc0093f4Seschrock symshndx_size = data->d_size /
278dc0093f4Seschrock sizeof (GElf_Word);
279dc0093f4Seschrock break;
280dc0093f4Seschrock }
281dc0093f4Seschrock }
282dc0093f4Seschrock }
283dc0093f4Seschrock
284dc0093f4Seschrock if ((scn = elf_getscn(tgt->dt_elf, tgt->dt_symidx)) == NULL)
285dc0093f4Seschrock die("%s: failed to get section information", tgt->dt_filename);
286dc0093f4Seschrock if (gelf_getshdr(scn, &shdr) == NULL)
287dc0093f4Seschrock die("%s: failed to get section header", tgt->dt_filename);
288dc0093f4Seschrock if (shdr.sh_entsize == 0)
289dc0093f4Seschrock die("%s: symbol table has zero size", tgt->dt_filename);
290dc0093f4Seschrock
291dc0093f4Seschrock if ((symdata = elf_getdata(scn, NULL)) == NULL)
292dc0093f4Seschrock die("%s: failed to get symbol table", tgt->dt_filename);
293dc0093f4Seschrock
294dc0093f4Seschrock tgt->dt_symcount = symdata->d_size / gelf_fsize(tgt->dt_elf, ELF_T_SYM,
295dc0093f4Seschrock 1, EV_CURRENT);
296dc0093f4Seschrock
297dc0093f4Seschrock p_symtab = safe_malloc(tgt->dt_symcount * sizeof (sym_entry_t));
298dc0093f4Seschrock
299dc0093f4Seschrock for (i = 0, sym = p_symtab; i < tgt->dt_symcount; i++) {
300dc0093f4Seschrock if (gelf_getsym(symdata, i, &(sym->se_sym)) == NULL) {
301dc0093f4Seschrock warn("%s: gelf_getsym returned NULL for %d",
302dc0093f4Seschrock tgt->dt_filename, i);
303dc0093f4Seschrock nsym++;
304dc0093f4Seschrock continue;
305dc0093f4Seschrock }
306dc0093f4Seschrock
307dc0093f4Seschrock /*
308dc0093f4Seschrock * We're only interested in data symbols.
309dc0093f4Seschrock */
310dc0093f4Seschrock if (!IS_DATA_TYPE(GELF_ST_TYPE(sym->se_sym.st_info))) {
311dc0093f4Seschrock nsym++;
312dc0093f4Seschrock continue;
313dc0093f4Seschrock }
314dc0093f4Seschrock
315dc0093f4Seschrock if (sym->se_sym.st_shndx == SHN_XINDEX && symshndx != NULL) {
316dc0093f4Seschrock if (i > symshndx_size) {
317dc0093f4Seschrock warn("%s: bad SHNX_XINDEX %d",
318dc0093f4Seschrock tgt->dt_filename, i);
319dc0093f4Seschrock sym->se_shndx = -1;
320dc0093f4Seschrock } else {
321dc0093f4Seschrock sym->se_shndx = symshndx[i];
322dc0093f4Seschrock }
323dc0093f4Seschrock } else {
324dc0093f4Seschrock sym->se_shndx = sym->se_sym.st_shndx;
325dc0093f4Seschrock }
326dc0093f4Seschrock
3276e6df3cfSJason King /* Deal with symbols with special section indicies */
3286e6df3cfSJason King if (sym->se_shndx == SHN_ABS) {
3296e6df3cfSJason King /*
3306e6df3cfSJason King * If st_value == 0, references to these
3316e6df3cfSJason King * symbols in code are modified in situ
3326e6df3cfSJason King * thus we will never attempt to look
3336e6df3cfSJason King * them up.
3346e6df3cfSJason King */
3356e6df3cfSJason King if (sym->se_sym.st_value == 0) {
3366e6df3cfSJason King /*
3376e6df3cfSJason King * References to these symbols in code
3386e6df3cfSJason King * are modified in situ by the runtime
3396e6df3cfSJason King * linker and no code on disk will ever
3406e6df3cfSJason King * attempt to look them up.
3416e6df3cfSJason King */
3426e6df3cfSJason King nsym++;
3436e6df3cfSJason King continue;
3446e6df3cfSJason King } else {
3456e6df3cfSJason King /*
3466e6df3cfSJason King * If st_value != 0, (such as examining
3476e6df3cfSJason King * something in /system/object/.../object)
3486e6df3cfSJason King * the values should resolve to a value
3496e6df3cfSJason King * within an existing section (such as
3506e6df3cfSJason King * .data). This also means it never needs
3516e6df3cfSJason King * to have st_value mapped.
3526e6df3cfSJason King */
3536e6df3cfSJason King sym++;
3546e6df3cfSJason King continue;
3556e6df3cfSJason King }
3566e6df3cfSJason King }
3576e6df3cfSJason King
3586e6df3cfSJason King /*
3596e6df3cfSJason King * Ignore the symbol if it has some other special
3606e6df3cfSJason King * section index
3616e6df3cfSJason King */
3626e6df3cfSJason King if (sym->se_shndx == SHN_UNDEF ||
3636e6df3cfSJason King sym->se_shndx >= SHN_LORESERVE) {
3646e6df3cfSJason King nsym++;
3656e6df3cfSJason King continue;
3666e6df3cfSJason King }
3676e6df3cfSJason King
368dc0093f4Seschrock if ((sym->se_name = elf_strptr(tgt->dt_elf, shdr.sh_link,
369dc0093f4Seschrock (size_t)sym->se_sym.st_name)) == NULL) {
370dc0093f4Seschrock warn("%s: failed to lookup symbol %d name",
371dc0093f4Seschrock tgt->dt_filename, i);
372dc0093f4Seschrock nsym++;
373dc0093f4Seschrock continue;
374dc0093f4Seschrock }
375dc0093f4Seschrock
376b5f3c6ffSJason King /*
377b5f3c6ffSJason King * If we had to map this section, its symbol value
378b5f3c6ffSJason King * also needs to be mapped.
379b5f3c6ffSJason King */
380b5f3c6ffSJason King if (tgt->dt_shnmap[sym->se_shndx].dm_mapped)
381b5f3c6ffSJason King sym->se_sym.st_value +=
382b5f3c6ffSJason King tgt->dt_shnmap[sym->se_shndx].dm_start;
383b5f3c6ffSJason King
384dc0093f4Seschrock sym++;
385dc0093f4Seschrock }
386dc0093f4Seschrock
387dc0093f4Seschrock tgt->dt_symcount -= nsym;
3887a65609eSjmcp tgt->dt_symtab = realloc(p_symtab, tgt->dt_symcount *
3897a65609eSjmcp sizeof (sym_entry_t));
390dc0093f4Seschrock
391dc0093f4Seschrock qsort(tgt->dt_symtab, tgt->dt_symcount, sizeof (sym_entry_t),
392dc0093f4Seschrock sym_compare);
393dc0093f4Seschrock }
394dc0093f4Seschrock
395dc0093f4Seschrock /*
396b5f3c6ffSJason King * Assign virtual address ranges for sections that need it
397b5f3c6ffSJason King */
398b5f3c6ffSJason King static void
create_addrmap(dis_tgt_t * tgt)399b5f3c6ffSJason King create_addrmap(dis_tgt_t *tgt)
400b5f3c6ffSJason King {
401b5f3c6ffSJason King uint64_t addr;
402b5f3c6ffSJason King int i;
403b5f3c6ffSJason King
404b5f3c6ffSJason King if (tgt->dt_shnmap == NULL)
405b5f3c6ffSJason King return;
406b5f3c6ffSJason King
407b5f3c6ffSJason King /* find the greatest used address */
408b5f3c6ffSJason King for (addr = 0, i = 1; i < tgt->dt_shncount; i++)
409b5f3c6ffSJason King if (tgt->dt_shnmap[i].dm_start > addr)
410b5f3c6ffSJason King addr = tgt->dt_shnmap[i].dm_start +
411b5f3c6ffSJason King tgt->dt_shnmap[i].dm_length;
412b5f3c6ffSJason King
413b5f3c6ffSJason King addr = P2ROUNDUP(addr, 0x1000);
414b5f3c6ffSJason King
415b5f3c6ffSJason King /*
416b5f3c6ffSJason King * Assign section a starting address beyond the largest mapped section
417b5f3c6ffSJason King * if no address was given.
418b5f3c6ffSJason King */
419b5f3c6ffSJason King for (i = 1; i < tgt->dt_shncount; i++) {
420b5f3c6ffSJason King if (tgt->dt_shnmap[i].dm_start != 0)
421b5f3c6ffSJason King continue;
422b5f3c6ffSJason King
423b5f3c6ffSJason King tgt->dt_shnmap[i].dm_start = addr;
424b5f3c6ffSJason King tgt->dt_shnmap[i].dm_mapped = B_TRUE;
425b5f3c6ffSJason King addr = P2ROUNDUP(addr + tgt->dt_shnmap[i].dm_length, 0x1000);
426b5f3c6ffSJason King }
427b5f3c6ffSJason King }
428b5f3c6ffSJason King
429b5f3c6ffSJason King /*
430dc0093f4Seschrock * Create a target backed by an ELF file.
431dc0093f4Seschrock */
432dc0093f4Seschrock dis_tgt_t *
dis_tgt_create(const char * file)433dc0093f4Seschrock dis_tgt_create(const char *file)
434dc0093f4Seschrock {
435dc0093f4Seschrock dis_tgt_t *tgt, *current;
436dc0093f4Seschrock int idx;
437dc0093f4Seschrock Elf *elf;
438dc0093f4Seschrock GElf_Ehdr ehdr;
439dc0093f4Seschrock Elf_Arhdr *arhdr = NULL;
440dc0093f4Seschrock int cmd;
441dc0093f4Seschrock
442dc0093f4Seschrock if (elf_version(EV_CURRENT) == EV_NONE)
443dc0093f4Seschrock die("libelf(3ELF) out of date");
444dc0093f4Seschrock
445dc0093f4Seschrock tgt = safe_malloc(sizeof (dis_tgt_t));
446dc0093f4Seschrock
447dc0093f4Seschrock if ((tgt->dt_fd = open(file, O_RDONLY)) < 0) {
448dc0093f4Seschrock warn("%s: failed opening file, reason: %s", file,
449dc0093f4Seschrock strerror(errno));
450dc0093f4Seschrock free(tgt);
451dc0093f4Seschrock return (NULL);
452dc0093f4Seschrock }
453dc0093f4Seschrock
454dc0093f4Seschrock if ((tgt->dt_elf_root =
455dc0093f4Seschrock elf_begin(tgt->dt_fd, ELF_C_READ, NULL)) == NULL) {
456dc0093f4Seschrock warn("%s: invalid or corrupt ELF file", file);
457dc0093f4Seschrock dis_tgt_destroy(tgt);
458dc0093f4Seschrock return (NULL);
459dc0093f4Seschrock }
460dc0093f4Seschrock
461dc0093f4Seschrock current = tgt;
462dc0093f4Seschrock cmd = ELF_C_READ;
463dc0093f4Seschrock while ((elf = elf_begin(tgt->dt_fd, cmd, tgt->dt_elf_root)) != NULL) {
46427553b5cSRichard Lowe size_t shnum = 0;
465dc0093f4Seschrock
466dc0093f4Seschrock if (elf_kind(tgt->dt_elf_root) == ELF_K_AR &&
467dc0093f4Seschrock (arhdr = elf_getarhdr(elf)) == NULL) {
468dc0093f4Seschrock warn("%s: malformed archive", file);
469dc0093f4Seschrock dis_tgt_destroy(tgt);
470dc0093f4Seschrock return (NULL);
471dc0093f4Seschrock }
472dc0093f4Seschrock
473dc0093f4Seschrock /*
474dc0093f4Seschrock * Make sure that this Elf file is sane
475dc0093f4Seschrock */
476dc0093f4Seschrock if (gelf_getehdr(elf, &ehdr) == NULL) {
477dc0093f4Seschrock if (arhdr != NULL) {
478dc0093f4Seschrock /*
479dc0093f4Seschrock * For archives, we drive on in the face of bad
480dc0093f4Seschrock * members. The "/" and "//" members are
481dc0093f4Seschrock * special, and should be silently ignored.
482dc0093f4Seschrock */
483dc0093f4Seschrock if (strcmp(arhdr->ar_name, "/") != 0 &&
484dc0093f4Seschrock strcmp(arhdr->ar_name, "//") != 0)
485dc0093f4Seschrock warn("%s[%s]: invalid file type",
486dc0093f4Seschrock file, arhdr->ar_name);
487dc0093f4Seschrock cmd = elf_next(elf);
488dc0093f4Seschrock (void) elf_end(elf);
489dc0093f4Seschrock continue;
490dc0093f4Seschrock }
491dc0093f4Seschrock
492dc0093f4Seschrock warn("%s: invalid file type", file);
493dc0093f4Seschrock dis_tgt_destroy(tgt);
494dc0093f4Seschrock return (NULL);
495dc0093f4Seschrock }
496dc0093f4Seschrock
497dc0093f4Seschrock /*
498dc0093f4Seschrock * If we're seeing a new Elf object, then we have an
499dc0093f4Seschrock * archive. In this case, we create a new target, and chain it
500dc0093f4Seschrock * off the master target. We can later iterate over these
501dc0093f4Seschrock * targets using dis_tgt_next().
502dc0093f4Seschrock */
503dc0093f4Seschrock if (current->dt_elf != NULL) {
504dc0093f4Seschrock dis_tgt_t *next = safe_malloc(sizeof (dis_tgt_t));
505dc0093f4Seschrock next->dt_elf_root = tgt->dt_elf_root;
506dc0093f4Seschrock next->dt_fd = -1;
507dc0093f4Seschrock current->dt_next = next;
508dc0093f4Seschrock current = next;
509dc0093f4Seschrock }
510dc0093f4Seschrock current->dt_elf = elf;
511dc0093f4Seschrock current->dt_arhdr = arhdr;
512dc0093f4Seschrock
51362b628a6SAli Bahrami if (elf_getshdrstrndx(elf, ¤t->dt_shstrndx) == -1) {
514dc0093f4Seschrock warn("%s: failed to get section string table for "
515dc0093f4Seschrock "file", file);
516dc0093f4Seschrock dis_tgt_destroy(tgt);
517dc0093f4Seschrock return (NULL);
518dc0093f4Seschrock }
519dc0093f4Seschrock
52027553b5cSRichard Lowe if (elf_getshdrnum(elf, &shnum) == -1) {
52127553b5cSRichard Lowe warn("%s: failed to get number of sections in file",
52227553b5cSRichard Lowe file);
52327553b5cSRichard Lowe dis_tgt_destroy(tgt);
52427553b5cSRichard Lowe return (NULL);
52527553b5cSRichard Lowe }
52627553b5cSRichard Lowe
527b5f3c6ffSJason King current->dt_shnmap = safe_malloc(sizeof (dis_shnmap_t) *
52827553b5cSRichard Lowe shnum);
52927553b5cSRichard Lowe current->dt_shncount = shnum;
530dc0093f4Seschrock
531b5f3c6ffSJason King idx = 0;
532b5f3c6ffSJason King dis_tgt_section_iter(current, tgt_scn_init, &idx);
5336e6df3cfSJason King current->dt_filename = file;
534b5f3c6ffSJason King
535b5f3c6ffSJason King create_addrmap(current);
536dc0093f4Seschrock if (current->dt_symidx != 0)
537dc0093f4Seschrock construct_symtab(current);
538dc0093f4Seschrock
539dc0093f4Seschrock cmd = elf_next(elf);
540dc0093f4Seschrock }
541dc0093f4Seschrock
542dc0093f4Seschrock /*
543dc0093f4Seschrock * Final sanity check. If we had an archive with no members, then bail
544dc0093f4Seschrock * out with a nice message.
545dc0093f4Seschrock */
546dc0093f4Seschrock if (tgt->dt_elf == NULL) {
547dc0093f4Seschrock warn("%s: empty archive\n", file);
548dc0093f4Seschrock dis_tgt_destroy(tgt);
549dc0093f4Seschrock return (NULL);
550dc0093f4Seschrock }
551dc0093f4Seschrock
552dc0093f4Seschrock return (tgt);
553dc0093f4Seschrock }
554dc0093f4Seschrock
555dc0093f4Seschrock /*
556dc0093f4Seschrock * Return the filename associated with the target.
557dc0093f4Seschrock */
558dc0093f4Seschrock const char *
dis_tgt_name(dis_tgt_t * tgt)559dc0093f4Seschrock dis_tgt_name(dis_tgt_t *tgt)
560dc0093f4Seschrock {
561dc0093f4Seschrock return (tgt->dt_filename);
562dc0093f4Seschrock }
563dc0093f4Seschrock
564dc0093f4Seschrock /*
565dc0093f4Seschrock * Return the archive member name, if any.
566dc0093f4Seschrock */
567dc0093f4Seschrock const char *
dis_tgt_member(dis_tgt_t * tgt)568dc0093f4Seschrock dis_tgt_member(dis_tgt_t *tgt)
569dc0093f4Seschrock {
570dc0093f4Seschrock if (tgt->dt_arhdr)
571dc0093f4Seschrock return (tgt->dt_arhdr->ar_name);
572dc0093f4Seschrock else
573dc0093f4Seschrock return (NULL);
574dc0093f4Seschrock }
575dc0093f4Seschrock
576dc0093f4Seschrock /*
577dc0093f4Seschrock * Return the Elf_Ehdr associated with this target. Needed to determine which
578dc0093f4Seschrock * disassembler to use.
579dc0093f4Seschrock */
580dc0093f4Seschrock void
dis_tgt_ehdr(dis_tgt_t * tgt,GElf_Ehdr * ehdr)581dc0093f4Seschrock dis_tgt_ehdr(dis_tgt_t *tgt, GElf_Ehdr *ehdr)
582dc0093f4Seschrock {
583dc0093f4Seschrock (void) gelf_getehdr(tgt->dt_elf, ehdr);
584dc0093f4Seschrock }
585dc0093f4Seschrock
586dc0093f4Seschrock /*
587dc0093f4Seschrock * Return the next target in the list, if this is an archive.
588dc0093f4Seschrock */
589dc0093f4Seschrock dis_tgt_t *
dis_tgt_next(dis_tgt_t * tgt)590dc0093f4Seschrock dis_tgt_next(dis_tgt_t *tgt)
591dc0093f4Seschrock {
592dc0093f4Seschrock return (tgt->dt_next);
593dc0093f4Seschrock }
594dc0093f4Seschrock
595dc0093f4Seschrock /*
596dc0093f4Seschrock * Destroy a target and free up any associated memory.
597dc0093f4Seschrock */
598dc0093f4Seschrock void
dis_tgt_destroy(dis_tgt_t * tgt)599dc0093f4Seschrock dis_tgt_destroy(dis_tgt_t *tgt)
600dc0093f4Seschrock {
601dc0093f4Seschrock dis_tgt_t *current, *next;
602dc0093f4Seschrock
603dc0093f4Seschrock current = tgt->dt_next;
604dc0093f4Seschrock while (current != NULL) {
605dc0093f4Seschrock next = current->dt_next;
606dc0093f4Seschrock if (current->dt_elf)
607dc0093f4Seschrock (void) elf_end(current->dt_elf);
608dc0093f4Seschrock if (current->dt_symtab)
609dc0093f4Seschrock free(current->dt_symtab);
610dc0093f4Seschrock free(current);
611dc0093f4Seschrock current = next;
612dc0093f4Seschrock }
613dc0093f4Seschrock
614dc0093f4Seschrock if (tgt->dt_elf)
615dc0093f4Seschrock (void) elf_end(tgt->dt_elf);
616dc0093f4Seschrock if (tgt->dt_elf_root)
617dc0093f4Seschrock (void) elf_end(tgt->dt_elf_root);
618dc0093f4Seschrock
619dc0093f4Seschrock if (tgt->dt_symtab)
620dc0093f4Seschrock free(tgt->dt_symtab);
621dc0093f4Seschrock
622dc0093f4Seschrock free(tgt);
623dc0093f4Seschrock }
624dc0093f4Seschrock
625dc0093f4Seschrock /*
626b5f3c6ffSJason King * Given an address, return the section it is in and set the offset within
627b5f3c6ffSJason King * the section.
628b5f3c6ffSJason King */
629b5f3c6ffSJason King const char *
dis_find_section(dis_tgt_t * tgt,uint64_t addr,off_t * offset)630b5f3c6ffSJason King dis_find_section(dis_tgt_t *tgt, uint64_t addr, off_t *offset)
631b5f3c6ffSJason King {
632b5f3c6ffSJason King int i;
633b5f3c6ffSJason King
634b5f3c6ffSJason King for (i = 1; i < tgt->dt_shncount; i++) {
635b5f3c6ffSJason King if ((addr >= tgt->dt_shnmap[i].dm_start) &&
636b5f3c6ffSJason King (addr < tgt->dt_shnmap[i].dm_start +
637b5f3c6ffSJason King tgt->dt_shnmap[i].dm_length)) {
638b5f3c6ffSJason King *offset = addr - tgt->dt_shnmap[i].dm_start;
639b5f3c6ffSJason King return (tgt->dt_shnmap[i].dm_name);
640b5f3c6ffSJason King }
641b5f3c6ffSJason King }
642b5f3c6ffSJason King
643b5f3c6ffSJason King *offset = 0;
644b5f3c6ffSJason King return (NULL);
645b5f3c6ffSJason King }
646b5f3c6ffSJason King
647b5f3c6ffSJason King /*
648dc0093f4Seschrock * Given an address, returns the name of the corresponding symbol, as well as
649dc0093f4Seschrock * the offset within that symbol. If no matching symbol is found, then NULL is
650dc0093f4Seschrock * returned.
651dc0093f4Seschrock *
652dc0093f4Seschrock * If 'cache_result' is specified, then we keep track of the resulting symbol.
653dc0093f4Seschrock * This cached result is consulted first on subsequent lookups in order to avoid
654dc0093f4Seschrock * unecessary lookups. This flag should be used for resolving the current PC,
655dc0093f4Seschrock * as the majority of addresses stay within the current function.
656dc0093f4Seschrock */
657dc0093f4Seschrock const char *
dis_tgt_lookup(dis_tgt_t * tgt,uint64_t addr,off_t * offset,int cache_result,size_t * size,int * isfunc)658dc0093f4Seschrock dis_tgt_lookup(dis_tgt_t *tgt, uint64_t addr, off_t *offset, int cache_result,
659dc0093f4Seschrock size_t *size, int *isfunc)
660dc0093f4Seschrock {
661dc0093f4Seschrock int lo, hi, mid;
662dc0093f4Seschrock sym_entry_t *sym, *osym, *match;
663dc0093f4Seschrock int found;
664dc0093f4Seschrock
665dc986d9fSRichard Lowe *offset = 0;
666dc986d9fSRichard Lowe *size = 0;
667dc986d9fSRichard Lowe if (isfunc != NULL)
668dc986d9fSRichard Lowe *isfunc = 0;
669dc986d9fSRichard Lowe
670dc0093f4Seschrock if (tgt->dt_symcache != NULL &&
671dc0093f4Seschrock addr >= tgt->dt_symcache->se_sym.st_value &&
672dc0093f4Seschrock addr < tgt->dt_symcache->se_sym.st_value +
673dc0093f4Seschrock tgt->dt_symcache->se_sym.st_size) {
674dc986d9fSRichard Lowe sym = tgt->dt_symcache;
675dc986d9fSRichard Lowe *offset = addr - sym->se_sym.st_value;
676dc986d9fSRichard Lowe *size = sym->se_sym.st_size;
677dc986d9fSRichard Lowe if (isfunc != NULL)
678dc986d9fSRichard Lowe *isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) ==
679dc986d9fSRichard Lowe STT_FUNC);
680dc986d9fSRichard Lowe return (sym->se_name);
681dc0093f4Seschrock }
682dc0093f4Seschrock
683dc0093f4Seschrock lo = 0;
684dc0093f4Seschrock hi = (tgt->dt_symcount - 1);
685dc0093f4Seschrock found = 0;
686dc0093f4Seschrock match = osym = NULL;
687dc0093f4Seschrock while (lo <= hi) {
688dc0093f4Seschrock mid = (lo + hi) / 2;
689dc0093f4Seschrock
690dc0093f4Seschrock sym = &tgt->dt_symtab[mid];
691dc0093f4Seschrock
692dc0093f4Seschrock if (addr >= sym->se_sym.st_value &&
693dc0093f4Seschrock addr < sym->se_sym.st_value + sym->se_sym.st_size &&
694dc0093f4Seschrock (!found || sym->se_sym.st_value > osym->se_sym.st_value)) {
695dc0093f4Seschrock osym = sym;
696dc0093f4Seschrock found = 1;
697dc0093f4Seschrock } else if (addr == sym->se_sym.st_value) {
698dc0093f4Seschrock /*
699dc0093f4Seschrock * Particularly for .plt objects, it's possible to have
700dc0093f4Seschrock * a zero sized object. We want to return this, but we
701dc0093f4Seschrock * want it to be a last resort.
702dc0093f4Seschrock */
703dc0093f4Seschrock match = sym;
704dc0093f4Seschrock }
705dc0093f4Seschrock
706dc0093f4Seschrock if (addr < sym->se_sym.st_value)
707dc0093f4Seschrock hi = mid - 1;
708dc0093f4Seschrock else
709dc0093f4Seschrock lo = mid + 1;
710dc0093f4Seschrock }
711dc0093f4Seschrock
712dc0093f4Seschrock if (!found) {
713dc0093f4Seschrock if (match)
714dc0093f4Seschrock osym = match;
715dc0093f4Seschrock else
716dc0093f4Seschrock return (NULL);
717dc0093f4Seschrock }
718dc0093f4Seschrock
719dc0093f4Seschrock /*
720dc0093f4Seschrock * Walk backwards to find the best match.
721dc0093f4Seschrock */
722dc0093f4Seschrock do {
723dc0093f4Seschrock sym = osym;
724dc0093f4Seschrock
725dc0093f4Seschrock if (osym == tgt->dt_symtab)
726dc0093f4Seschrock break;
727dc0093f4Seschrock
728dc0093f4Seschrock osym = osym - 1;
729dc0093f4Seschrock } while ((sym->se_sym.st_value == osym->se_sym.st_value) &&
730dc0093f4Seschrock (addr >= osym->se_sym.st_value) &&
731dc0093f4Seschrock (addr < osym->se_sym.st_value + osym->se_sym.st_size));
732dc0093f4Seschrock
733dc0093f4Seschrock if (cache_result)
734dc0093f4Seschrock tgt->dt_symcache = sym;
735dc0093f4Seschrock
736dc0093f4Seschrock *offset = addr - sym->se_sym.st_value;
737dc0093f4Seschrock *size = sym->se_sym.st_size;
738dc0093f4Seschrock if (isfunc)
739dc0093f4Seschrock *isfunc = (GELF_ST_TYPE(sym->se_sym.st_info) == STT_FUNC);
740dc0093f4Seschrock
741dc0093f4Seschrock return (sym->se_name);
742dc0093f4Seschrock }
743dc0093f4Seschrock
744dc0093f4Seschrock /*
745dc0093f4Seschrock * Given an address, return the starting offset of the next symbol in the file.
7466e6df3cfSJason King * Only needed on variable length instruction architectures.
747dc0093f4Seschrock */
748dc0093f4Seschrock off_t
dis_tgt_next_symbol(dis_tgt_t * tgt,uint64_t addr)749dc0093f4Seschrock dis_tgt_next_symbol(dis_tgt_t *tgt, uint64_t addr)
750dc0093f4Seschrock {
751b5f3c6ffSJason King sym_entry_t *sym;
752dc0093f4Seschrock
753edd4ab01SRichard Lowe sym = (tgt->dt_symcache != NULL) ? tgt->dt_symcache : tgt->dt_symtab;
754edd4ab01SRichard Lowe
755edd4ab01SRichard Lowe while (sym != (tgt->dt_symtab + tgt->dt_symcount)) {
756b5f3c6ffSJason King if (sym->se_sym.st_value >= addr)
757dc0093f4Seschrock return (sym->se_sym.st_value - addr);
758edd4ab01SRichard Lowe sym++;
759dc0093f4Seschrock }
760dc0093f4Seschrock
761b5f3c6ffSJason King return (0);
762b5f3c6ffSJason King }
763b5f3c6ffSJason King
764dc0093f4Seschrock /*
765dc0093f4Seschrock * Iterate over all sections in the target, executing the given callback for
766dc0093f4Seschrock * each.
767dc0093f4Seschrock */
768dc0093f4Seschrock void
dis_tgt_section_iter(dis_tgt_t * tgt,section_iter_f func,void * data)769dc0093f4Seschrock dis_tgt_section_iter(dis_tgt_t *tgt, section_iter_f func, void *data)
770dc0093f4Seschrock {
771dc0093f4Seschrock dis_scn_t sdata;
772dc0093f4Seschrock Elf_Scn *scn;
773dc0093f4Seschrock int idx;
774dc0093f4Seschrock
775dc0093f4Seschrock for (scn = elf_nextscn(tgt->dt_elf, NULL), idx = 1; scn != NULL;
776dc0093f4Seschrock scn = elf_nextscn(tgt->dt_elf, scn), idx++) {
777dc0093f4Seschrock
778dc0093f4Seschrock if (gelf_getshdr(scn, &sdata.ds_shdr) == NULL) {
779dc0093f4Seschrock warn("%s: failed to get section %d header",
780dc0093f4Seschrock tgt->dt_filename, idx);
781dc0093f4Seschrock continue;
782dc0093f4Seschrock }
783dc0093f4Seschrock
784dc0093f4Seschrock if ((sdata.ds_name = elf_strptr(tgt->dt_elf, tgt->dt_shstrndx,
785dc0093f4Seschrock sdata.ds_shdr.sh_name)) == NULL) {
786dc0093f4Seschrock warn("%s: failed to get section %d name",
787dc0093f4Seschrock tgt->dt_filename, idx);
788dc0093f4Seschrock continue;
789dc0093f4Seschrock }
790dc0093f4Seschrock
791dc0093f4Seschrock if ((sdata.ds_data = elf_getdata(scn, NULL)) == NULL) {
792dc0093f4Seschrock warn("%s: failed to get data for section '%s'",
793dc0093f4Seschrock tgt->dt_filename, sdata.ds_name);
794dc0093f4Seschrock continue;
795dc0093f4Seschrock }
796dc0093f4Seschrock
797b5f3c6ffSJason King /*
798b5f3c6ffSJason King * dis_tgt_section_iter is also used before the section map
799b5f3c6ffSJason King * is initialized, so only check when we need to. If the
800b5f3c6ffSJason King * section map is uninitialized, it will return 0 and have
801b5f3c6ffSJason King * no net effect.
802b5f3c6ffSJason King */
803b5f3c6ffSJason King if (sdata.ds_shdr.sh_addr == 0)
804b5f3c6ffSJason King sdata.ds_shdr.sh_addr = tgt->dt_shnmap[idx].dm_start;
805b5f3c6ffSJason King
806dc0093f4Seschrock func(tgt, &sdata, data);
807dc0093f4Seschrock }
808dc0093f4Seschrock }
809dc0093f4Seschrock
810dc0093f4Seschrock /*
811dc0093f4Seschrock * Return 1 if the given section contains text, 0 otherwise.
812dc0093f4Seschrock */
813dc0093f4Seschrock int
dis_section_istext(dis_scn_t * scn)814dc0093f4Seschrock dis_section_istext(dis_scn_t *scn)
815dc0093f4Seschrock {
816dc0093f4Seschrock return ((scn->ds_shdr.sh_type == SHT_PROGBITS) &&
817dc0093f4Seschrock (scn->ds_shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR)));
818dc0093f4Seschrock }
819dc0093f4Seschrock
820dc0093f4Seschrock /*
821dc0093f4Seschrock * Return a pointer to the section data.
822dc0093f4Seschrock */
823dc0093f4Seschrock void *
dis_section_data(dis_scn_t * scn)824dc0093f4Seschrock dis_section_data(dis_scn_t *scn)
825dc0093f4Seschrock {
826dc0093f4Seschrock return (scn->ds_data->d_buf);
827dc0093f4Seschrock }
828dc0093f4Seschrock
829dc0093f4Seschrock /*
830dc0093f4Seschrock * Return the size of the section data.
831dc0093f4Seschrock */
832dc0093f4Seschrock size_t
dis_section_size(dis_scn_t * scn)833dc0093f4Seschrock dis_section_size(dis_scn_t *scn)
834dc0093f4Seschrock {
835dc0093f4Seschrock return (scn->ds_data->d_size);
836dc0093f4Seschrock }
837dc0093f4Seschrock
838dc0093f4Seschrock /*
839dc0093f4Seschrock * Return the address for the given section.
840dc0093f4Seschrock */
841dc0093f4Seschrock uint64_t
dis_section_addr(dis_scn_t * scn)842dc0093f4Seschrock dis_section_addr(dis_scn_t *scn)
843dc0093f4Seschrock {
844dc0093f4Seschrock return (scn->ds_shdr.sh_addr);
845dc0093f4Seschrock }
846dc0093f4Seschrock
847dc0093f4Seschrock /*
848dc0093f4Seschrock * Return the name of the current section.
849dc0093f4Seschrock */
850dc0093f4Seschrock const char *
dis_section_name(dis_scn_t * scn)851dc0093f4Seschrock dis_section_name(dis_scn_t *scn)
852dc0093f4Seschrock {
853dc0093f4Seschrock return (scn->ds_name);
854dc0093f4Seschrock }
855dc0093f4Seschrock
856dc0093f4Seschrock /*
857dc0093f4Seschrock * Create an allocated copy of the given section
858dc0093f4Seschrock */
859dc0093f4Seschrock dis_scn_t *
dis_section_copy(dis_scn_t * scn)860dc0093f4Seschrock dis_section_copy(dis_scn_t *scn)
861dc0093f4Seschrock {
862dc0093f4Seschrock dis_scn_t *new;
863dc0093f4Seschrock
864dc0093f4Seschrock new = safe_malloc(sizeof (dis_scn_t));
865dc0093f4Seschrock (void) memcpy(new, scn, sizeof (dis_scn_t));
866dc0093f4Seschrock
867dc0093f4Seschrock return (new);
868dc0093f4Seschrock }
869dc0093f4Seschrock
870dc0093f4Seschrock /*
871dc0093f4Seschrock * Free section memory
872dc0093f4Seschrock */
873dc0093f4Seschrock void
dis_section_free(dis_scn_t * scn)874dc0093f4Seschrock dis_section_free(dis_scn_t *scn)
875dc0093f4Seschrock {
876dc0093f4Seschrock free(scn);
877dc0093f4Seschrock }
878dc0093f4Seschrock
879dc0093f4Seschrock /*
880dc0093f4Seschrock * Iterate over all functions in the target, executing the given callback for
881dc0093f4Seschrock * each one.
882dc0093f4Seschrock */
883dc0093f4Seschrock void
dis_tgt_function_iter(dis_tgt_t * tgt,function_iter_f func,void * data)884dc0093f4Seschrock dis_tgt_function_iter(dis_tgt_t *tgt, function_iter_f func, void *data)
885dc0093f4Seschrock {
886dc0093f4Seschrock int i;
887dc0093f4Seschrock sym_entry_t *sym;
888dc0093f4Seschrock dis_func_t df;
889dc0093f4Seschrock Elf_Scn *scn;
890dc0093f4Seschrock GElf_Shdr shdr;
891dc0093f4Seschrock
892dc0093f4Seschrock for (i = 0, sym = tgt->dt_symtab; i < tgt->dt_symcount; i++, sym++) {
893dc0093f4Seschrock
894dc0093f4Seschrock /* ignore non-functions */
895dc0093f4Seschrock if ((GELF_ST_TYPE(sym->se_sym.st_info) != STT_FUNC) ||
896161d9479Srie (sym->se_name == NULL) ||
897161d9479Srie (sym->se_sym.st_size == 0) ||
898161d9479Srie (sym->se_shndx >= SHN_LORESERVE))
899dc0093f4Seschrock continue;
900dc0093f4Seschrock
901dc0093f4Seschrock /* get the ELF data associated with this function */
902dc0093f4Seschrock if ((scn = elf_getscn(tgt->dt_elf, sym->se_shndx)) == NULL ||
903dc0093f4Seschrock gelf_getshdr(scn, &shdr) == NULL ||
904dc0093f4Seschrock (df.df_data = elf_getdata(scn, NULL)) == NULL ||
905dc0093f4Seschrock df.df_data->d_size == 0) {
906dc0093f4Seschrock warn("%s: failed to read section %d",
907dc0093f4Seschrock tgt->dt_filename, sym->se_shndx);
908dc0093f4Seschrock continue;
909dc0093f4Seschrock }
910dc0093f4Seschrock
911b5f3c6ffSJason King if (tgt->dt_shnmap[sym->se_shndx].dm_mapped)
912b5f3c6ffSJason King shdr.sh_addr = tgt->dt_shnmap[sym->se_shndx].dm_start;
913b5f3c6ffSJason King
914dc0093f4Seschrock /*
915dc0093f4Seschrock * Verify that the address lies within the section that we think
916dc0093f4Seschrock * it does.
917dc0093f4Seschrock */
918dc0093f4Seschrock if (sym->se_sym.st_value < shdr.sh_addr ||
919dc0093f4Seschrock (sym->se_sym.st_value + sym->se_sym.st_size) >
920dc0093f4Seschrock (shdr.sh_addr + shdr.sh_size)) {
921dc0093f4Seschrock warn("%s: bad section %d for address %p",
922dc0093f4Seschrock tgt->dt_filename, sym->se_sym.st_shndx,
923dc0093f4Seschrock sym->se_sym.st_value);
924dc0093f4Seschrock continue;
925dc0093f4Seschrock }
926dc0093f4Seschrock
927dc0093f4Seschrock df.df_sym = sym;
928dc0093f4Seschrock df.df_offset = sym->se_sym.st_value - shdr.sh_addr;
929dc0093f4Seschrock
930dc0093f4Seschrock func(tgt, &df, data);
931dc0093f4Seschrock }
932dc0093f4Seschrock }
933dc0093f4Seschrock
934dc0093f4Seschrock /*
935dc0093f4Seschrock * Return the data associated with a given function.
936dc0093f4Seschrock */
937dc0093f4Seschrock void *
dis_function_data(dis_func_t * func)938dc0093f4Seschrock dis_function_data(dis_func_t *func)
939dc0093f4Seschrock {
940dc0093f4Seschrock return ((char *)func->df_data->d_buf + func->df_offset);
941dc0093f4Seschrock }
942dc0093f4Seschrock
943dc0093f4Seschrock /*
944dc0093f4Seschrock * Return the size of a function.
945dc0093f4Seschrock */
946dc0093f4Seschrock size_t
dis_function_size(dis_func_t * func)947dc0093f4Seschrock dis_function_size(dis_func_t *func)
948dc0093f4Seschrock {
949dc0093f4Seschrock return (func->df_sym->se_sym.st_size);
950dc0093f4Seschrock }
951dc0093f4Seschrock
952dc0093f4Seschrock /*
953dc0093f4Seschrock * Return the address of a function.
954dc0093f4Seschrock */
955dc0093f4Seschrock uint64_t
dis_function_addr(dis_func_t * func)956dc0093f4Seschrock dis_function_addr(dis_func_t *func)
957dc0093f4Seschrock {
958dc0093f4Seschrock return (func->df_sym->se_sym.st_value);
959dc0093f4Seschrock }
960dc0093f4Seschrock
961dc0093f4Seschrock /*
962dc0093f4Seschrock * Return the name of the function
963dc0093f4Seschrock */
964dc0093f4Seschrock const char *
dis_function_name(dis_func_t * func)965dc0093f4Seschrock dis_function_name(dis_func_t *func)
966dc0093f4Seschrock {
967dc0093f4Seschrock return (func->df_sym->se_name);
968dc0093f4Seschrock }
969dc0093f4Seschrock
970dc0093f4Seschrock /*
971dc0093f4Seschrock * Return a copy of a function.
972dc0093f4Seschrock */
973dc0093f4Seschrock dis_func_t *
dis_function_copy(dis_func_t * func)974dc0093f4Seschrock dis_function_copy(dis_func_t *func)
975dc0093f4Seschrock {
976dc0093f4Seschrock dis_func_t *new;
977dc0093f4Seschrock
978dc0093f4Seschrock new = safe_malloc(sizeof (dis_func_t));
979dc0093f4Seschrock (void) memcpy(new, func, sizeof (dis_func_t));
980dc0093f4Seschrock
981dc0093f4Seschrock return (new);
982dc0093f4Seschrock }
983dc0093f4Seschrock
984dc0093f4Seschrock /*
985dc0093f4Seschrock * Free function memory
986dc0093f4Seschrock */
987dc0093f4Seschrock void
dis_function_free(dis_func_t * func)988dc0093f4Seschrock dis_function_free(dis_func_t *func)
989dc0093f4Seschrock {
990dc0093f4Seschrock free(func);
991dc0093f4Seschrock }
992