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 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#include <sys/isa_defs.h>
27#include <sys/link.h>
28#include <strings.h>
29#include <stdlib.h>
30
31#include <mdb/mdb_debug.h>
32#include <mdb/mdb_modapi.h>
33#include <mdb/mdb_io_impl.h>
34#include <mdb/mdb_gelf.h>
35#include <mdb/mdb_err.h>
36#include <mdb/mdb.h>
37
38#define	GST_GROW	2	/* Mutable symbol table growth multiplier */
39#define	GST_DEFSZ	16	/* Mutable symbol table initial size */
40
41#define	GST_NVFLG	(MDB_NV_EXTNAME | MDB_NV_SILENT)
42
43static const char *gelf_strtab;	/* Active string table for qsort callbacks */
44
45static mdb_gelf_file_t *
46gelf_sect_init(mdb_gelf_file_t *gf)
47{
48	mdb_gelf_sect_t *gsp, *shstr = &gf->gf_sects[gf->gf_shstrndx];
49	size_t i;
50	GElf_Half npbit = 0;
51	GElf_Shdr *shp;
52	GElf_Phdr *gpp;
53
54	if (gf->gf_mode == GF_PROGRAM)
55		gf->gf_shnum = 0; /* Simplifies other code paths */
56
57	if (gf->gf_shnum == 0)
58		return (gf); /* If no section headers we're done here */
59
60	if (IOP_SEEK(gf->gf_io, shstr->gs_shdr.sh_offset, SEEK_SET) == -1) {
61		warn("failed to seek %s to shdr strings", IOP_NAME(gf->gf_io));
62		return (NULL);
63	}
64
65	shstr->gs_data = mdb_zalloc(shstr->gs_shdr.sh_size + 1, UM_SLEEP);
66
67	if (IOP_READ(gf->gf_io, shstr->gs_data, shstr->gs_shdr.sh_size) !=
68	    shstr->gs_shdr.sh_size) {
69		warn("failed to read %s shdr strings", IOP_NAME(gf->gf_io));
70		mdb_free(shstr->gs_data, shstr->gs_shdr.sh_size);
71		return (NULL);
72	}
73
74	for (gsp = gf->gf_sects, i = 0; i < gf->gf_shnum; i++, gsp++) {
75		shp = &gsp->gs_shdr;
76		gsp->gs_name = (const char *)shstr->gs_data + shp->sh_name;
77
78		if (shp->sh_name >= shstr->gs_shdr.sh_size) {
79			warn("section name for %s:[%u] is corrupt: %u\n",
80			    IOP_NAME(gf->gf_io), i, shp->sh_name);
81			gsp->gs_name = shstr->gs_data; /* empty string */
82		}
83
84		if (shp->sh_type == SHT_PROGBITS && (shp->sh_flags & SHF_ALLOC))
85			npbit++; /* Keep count for ET_REL code below */
86	}
87
88	/*
89	 * If the file is of type ET_REL, we would still like to provide file
90	 * i/o using the mdb_gelf_rw() function defined below.  To simplify
91	 * things, we forge up a sequence of Phdrs based on Shdrs which have
92	 * been marked SHF_ALLOC and are of type SHT_PROGBITS.  We convert
93	 * relevant Shdr fields to their Phdr equivalents, and then set the
94	 * p_vaddr (virtual base address) to the section's file offset.
95	 * This allows us to relocate a given symbol by simply incrementing
96	 * its st_value by the file offset of the section corresponding to
97	 * its st_shndx, and then perform i/o to read or write the symbol's
98	 * value in the object file.
99	 */
100	if (gf->gf_ehdr.e_type == ET_REL && npbit != 0) {
101		gf->gf_phdrs = mdb_zalloc(sizeof (GElf_Phdr) * npbit, UM_SLEEP);
102		gf->gf_phnum = npbit;
103		gf->gf_npload = npbit;
104
105		gpp = gf->gf_phdrs;
106		gsp = gf->gf_sects;
107
108		for (i = 0; i < gf->gf_shnum; i++, gsp++) {
109			shp = &gsp->gs_shdr;
110
111			if ((shp->sh_type == SHT_PROGBITS) &&
112			    (shp->sh_flags & SHF_ALLOC)) {
113				gpp->p_type = PT_LOAD;
114				gpp->p_flags = PF_R;
115
116				if (shp->sh_flags & SHF_EXECINSTR)
117					gpp->p_flags |= PF_X;
118				if (shp->sh_flags & SHF_WRITE)
119					gpp->p_flags |= PF_W;
120
121				gpp->p_offset = shp->sh_offset;
122				gpp->p_vaddr = shp->sh_offset;
123				gpp->p_filesz = shp->sh_size;
124				gpp->p_memsz = shp->sh_size;
125				gpp->p_align = shp->sh_addralign;
126
127				gpp++;
128			}
129		}
130	}
131
132	return (gf);
133}
134
135void *
136mdb_gelf_sect_load(mdb_gelf_file_t *gf, mdb_gelf_sect_t *gsp)
137{
138	ssize_t nbytes;
139
140	if (gsp->gs_data != NULL)
141		return (gsp->gs_data);
142
143	mdb_dprintf(MDB_DBG_ELF, "loading %s:%s (%lu bytes)\n",
144	    IOP_NAME(gf->gf_io), gsp->gs_name, (ulong_t)gsp->gs_shdr.sh_size);
145
146	gsp->gs_data = mdb_alloc(gsp->gs_shdr.sh_size, UM_SLEEP);
147
148	if (IOP_SEEK(gf->gf_io, gsp->gs_shdr.sh_offset, SEEK_SET) == -1) {
149		warn("failed to seek to start of %s:%s",
150		    IOP_NAME(gf->gf_io), gsp->gs_name);
151		goto err;
152	}
153
154	nbytes = IOP_READ(gf->gf_io, gsp->gs_data, gsp->gs_shdr.sh_size);
155
156	if (nbytes < 0) {
157		warn("failed to read %s:%s", IOP_NAME(gf->gf_io), gsp->gs_name);
158		goto err;
159	}
160
161	if (nbytes < gsp->gs_shdr.sh_size) {
162		mdb_dprintf(MDB_DBG_ELF, "only %ld of %llu bytes of %s:%s "
163		    "could be read\n", (long)nbytes, (u_longlong_t)
164		    gsp->gs_shdr.sh_size, IOP_NAME(gf->gf_io), gsp->gs_name);
165		bzero((char *)gsp->gs_data + nbytes,
166		    (size_t)gsp->gs_shdr.sh_size - nbytes);
167	}
168
169	return (gsp->gs_data);
170
171err:
172	mdb_free(gsp->gs_data, sizeof (gsp->gs_shdr.sh_size));
173	gsp->gs_data = NULL;
174	return (NULL);
175}
176
177void
178mdb_gelf_ehdr_to_gehdr(Ehdr *src, GElf_Ehdr *dst)
179{
180	bcopy(src->e_ident, dst->e_ident, sizeof (dst->e_ident));
181	dst->e_type = src->e_type;
182	dst->e_machine = src->e_machine;
183	dst->e_version = src->e_version;
184	dst->e_entry = src->e_entry;
185	dst->e_phoff = src->e_phoff;
186	dst->e_shoff = src->e_shoff;
187	dst->e_flags = src->e_flags;
188	dst->e_ehsize = src->e_ehsize;
189	dst->e_phentsize = src->e_phentsize;
190	dst->e_phnum = src->e_phnum;
191	dst->e_shentsize = src->e_shentsize;
192	dst->e_shnum = src->e_shnum;
193	dst->e_shstrndx = src->e_shstrndx;
194}
195
196static GElf_Shdr *
197gelf32_to_shdr(const Elf32_Shdr *src, GElf_Shdr *dst)
198{
199	if (src != NULL) {
200		dst->sh_name = src->sh_name;
201		dst->sh_type = src->sh_type;
202		dst->sh_flags = src->sh_flags;
203		dst->sh_addr = src->sh_addr;
204		dst->sh_offset = src->sh_offset;
205		dst->sh_size = src->sh_size;
206		dst->sh_link = src->sh_link;
207		dst->sh_info = src->sh_info;
208		dst->sh_addralign = src->sh_addralign;
209		dst->sh_entsize = src->sh_entsize;
210
211		return (dst);
212	}
213
214	return (NULL);
215}
216
217static GElf_Shdr *
218gelf64_to_shdr(const Elf64_Shdr *src, GElf_Shdr *dst)
219{
220	if (src != NULL) {
221		bcopy(src, dst, sizeof (Elf64_Shdr));
222		return (dst);
223	}
224
225	return (NULL);
226}
227
228static mdb_gelf_file_t *
229gelf_shdrs_init(mdb_gelf_file_t *gf, size_t shdr_size,
230    GElf_Shdr *(*elf2gelf)(const void *, GElf_Shdr *))
231{
232	caddr_t shdrs, shp;
233	size_t i;
234
235	mdb_gelf_sect_t *gsp;
236	size_t nbytes;
237
238	mdb_dprintf(MDB_DBG_ELF, "loading %s section headers (%u entries)\n",
239	    IOP_NAME(gf->gf_io), gf->gf_shnum);
240
241	if (gf->gf_shnum == 0)
242		return (gf);
243
244	if (IOP_SEEK(gf->gf_io, (off64_t)gf->gf_ehdr.e_shoff, SEEK_SET) == -1) {
245		warn("failed to seek %s to shdrs", IOP_NAME(gf->gf_io));
246		return (NULL);
247	}
248
249	nbytes = shdr_size * gf->gf_shnum;
250	shdrs = mdb_alloc(nbytes, UM_SLEEP);
251
252	if (IOP_READ(gf->gf_io, shdrs, nbytes) != nbytes) {
253		warn("failed to read %s section headers", IOP_NAME(gf->gf_io));
254		mdb_free(shdrs, nbytes);
255		return (NULL);
256	}
257
258	gf->gf_sects = mdb_zalloc(sizeof (mdb_gelf_sect_t) * gf->gf_shnum,
259	    UM_SLEEP);
260
261	shp = shdrs;
262	gsp = gf->gf_sects;
263
264	for (i = 0; i < gf->gf_shnum; i++, shp += shdr_size, gsp++)
265		(void) elf2gelf(shp, &gsp->gs_shdr);
266
267	mdb_free(shdrs, nbytes);
268	return (gf);
269}
270
271static GElf_Phdr *
272gelf32_to_phdr(const Elf32_Phdr *src, GElf_Phdr *dst)
273{
274	if (src != NULL) {
275		dst->p_type = src->p_type;
276		dst->p_offset = src->p_offset;
277		dst->p_vaddr = src->p_vaddr;
278		dst->p_paddr = src->p_paddr;
279		dst->p_filesz = src->p_filesz;
280		dst->p_memsz = src->p_memsz;
281		dst->p_flags = src->p_flags;
282		dst->p_align = src->p_align;
283
284		return (dst);
285	}
286
287	return (NULL);
288}
289
290static GElf_Phdr *
291gelf64_to_phdr(const Elf64_Phdr *src, GElf_Phdr *dst)
292{
293	if (src != NULL) {
294		bcopy(src, dst, sizeof (Elf64_Phdr));
295		return (dst);
296	}
297
298	return (NULL);
299}
300
301static int
302gelf_phdr_compare(const void *lp, const void *rp)
303{
304	GElf_Phdr *lhs = (GElf_Phdr *)lp;
305	GElf_Phdr *rhs = (GElf_Phdr *)rp;
306
307	/*
308	 * If both p_type fields are PT_LOAD, we want to sort by vaddr.
309	 * Exception is that p_vaddr == 0 means ignore this (put at end).
310	 */
311	if (lhs->p_type == PT_LOAD && rhs->p_type == PT_LOAD) {
312		if (lhs->p_vaddr != rhs->p_vaddr) {
313			if (lhs->p_vaddr == 0)
314				return (1); /* lhs is "greater" */
315
316			if (rhs->p_vaddr == 0)
317				return (-1); /* rhs is "greater" */
318
319			return (lhs->p_vaddr > rhs->p_vaddr ? 1 : -1);
320		}
321
322		return (0);
323	}
324
325	/*
326	 * If the p_type fields don't match, we need to make sure that PT_LOAD
327	 * entries are considered "less" (i.e. move towards the beginning
328	 * of the array we are sorting)
329	 */
330	if (lhs->p_type != rhs->p_type) {
331		if (lhs->p_type == PT_LOAD)
332			return (-1); /* rhs is "greater" */
333
334		if (rhs->p_type == PT_LOAD)
335			return (1); /* lhs is "greater" */
336
337		return (lhs->p_type > rhs->p_type ? 1 : -1);
338	}
339
340	/*
341	 * If the p_type is the same but neither is PT_LOAD, then
342	 * just sort by file offset (doesn't really matter)
343	 */
344	if (lhs->p_offset != rhs->p_offset)
345		return (lhs->p_offset > rhs->p_offset ? 1 : -1);
346
347	return (0);
348}
349
350static mdb_gelf_file_t *
351gelf_phdrs_init(mdb_gelf_file_t *gf, size_t phdr_size,
352    GElf_Phdr *(*elf2gelf)(const void *, GElf_Phdr *))
353{
354	caddr_t phdrs, php;
355	size_t i;
356
357	GElf_Phdr *gpp;
358	size_t nbytes;
359
360	mdb_dprintf(MDB_DBG_ELF, "loading %s program headers (%lu entries)\n",
361	    IOP_NAME(gf->gf_io), gf->gf_phnum);
362
363	if (gf->gf_phnum == 0)
364		return (gf);
365
366	if (IOP_SEEK(gf->gf_io, (off64_t)gf->gf_ehdr.e_phoff, SEEK_SET) == -1) {
367		warn("failed to seek %s to phdrs", IOP_NAME(gf->gf_io));
368		return (NULL);
369	}
370
371	nbytes = phdr_size * gf->gf_phnum;
372	phdrs = mdb_alloc(nbytes, UM_SLEEP);
373
374	if (IOP_READ(gf->gf_io, phdrs, nbytes) != nbytes) {
375		warn("failed to read %s program headers", IOP_NAME(gf->gf_io));
376		mdb_free(phdrs, nbytes);
377		return (NULL);
378	}
379
380	gf->gf_phdrs = mdb_zalloc(sizeof (GElf_Phdr) * gf->gf_phnum, UM_SLEEP);
381
382	php = phdrs;
383	gpp = gf->gf_phdrs;
384
385	/*
386	 * Iterate through the list of phdrs locating those that are of type
387	 * PT_LOAD; increment gf_npload so we know how many are loadable.
388	 */
389	for (i = 0; i < gf->gf_phnum; i++, php += phdr_size, gpp++) {
390		(void) elf2gelf(php, gpp);
391		if (gpp->p_type != PT_LOAD)
392			continue;
393
394		mdb_dprintf(MDB_DBG_ELF, "PT_LOAD va=0x%llx flags=0x%x "
395		    "memsz=%llu filesz=%llu off=%llu\n", (u_longlong_t)
396		    gpp->p_vaddr, gpp->p_flags, (u_longlong_t)gpp->p_memsz,
397		    (u_longlong_t)gpp->p_filesz, (u_longlong_t)gpp->p_offset);
398
399		gf->gf_npload++;
400	}
401
402	/*
403	 * Now we sort the phdrs array using a comparison routine which
404	 * arranges for the PT_LOAD phdrs with non-zero virtual addresses
405	 * to come first sorted by virtual address.  This means that we
406	 * can access the complete phdr table by examining the array
407	 * gf->gf_phdrs[0 .. gf->gf_phnum - 1], and we can access a sorted
408	 * array of valid PT_LOAD pdhrs by examining the array
409	 * gf->gf_phdrs[0 .. gf->gf_npload - 1].
410	 */
411	qsort(gf->gf_phdrs, gf->gf_phnum, sizeof (GElf_Phdr),
412	    gelf_phdr_compare);
413
414	/*
415	 * Locate the PT_DYNAMIC Phdr if one is present; we save this
416	 * Phdr pointer in gf->gf_dynp for future use.
417	 */
418	for (gpp = gf->gf_phdrs, i = 0; i < gf->gf_phnum; i++, gpp++) {
419		if (gpp->p_type == PT_DYNAMIC) {
420			mdb_dprintf(MDB_DBG_ELF, "PT_DYNAMIC "
421			    "filesize = %lluULL off=%lluULL\n",
422			    (u_longlong_t)gpp->p_filesz,
423			    (u_longlong_t)gpp->p_offset);
424
425			gf->gf_dynp = gpp;
426			break;
427		}
428	}
429
430	mdb_free(phdrs, nbytes);
431	return (gf);
432}
433
434static GElf_Dyn *
435gelf32_to_dyn(const Elf32_Dyn *src, GElf_Dyn *dst)
436{
437	if (src != NULL) {
438		dst->d_tag = (GElf_Xword)(Elf32_Word)src->d_tag;
439		dst->d_un.d_ptr = src->d_un.d_ptr;
440		return (dst);
441	}
442
443	return (NULL);
444}
445
446static GElf_Dyn *
447gelf64_to_dyn(const Elf64_Dyn *src, GElf_Dyn *dst)
448{
449	if (src != NULL) {
450		bcopy(src, dst, sizeof (Elf64_Dyn));
451		return (dst);
452	}
453
454	return (NULL);
455}
456
457static GElf_Xword
458gelf_dyn_lookup(mdb_gelf_file_t *gf, GElf_Xword tag)
459{
460	size_t i;
461
462	for (i = 0; i < gf->gf_ndyns; i++) {
463		if (gf->gf_dyns[i].d_tag == tag)
464			return (gf->gf_dyns[i].d_un.d_val);
465	}
466
467	return ((GElf_Xword)-1L);
468}
469
470void
471mdb_gelf_dyns_set(mdb_gelf_file_t *gf, void *dyns, size_t dyns_sz)
472{
473	size_t ndyns, i, dyn_size;
474	caddr_t dp;
475	GElf_Dyn *gdp;
476
477	if (gf->gf_dyns != NULL) {
478		/* Free the existing dyn entries */
479		free(gf->gf_dyns);
480		gf->gf_dyns = NULL;
481		gf->gf_ndyns = 0;
482	}
483
484	if (gf->gf_ehdr.e_ident[EI_CLASS] == ELFCLASS32)
485		dyn_size = sizeof (Elf32_Dyn);
486	else
487		dyn_size = sizeof (Elf64_Dyn);
488
489	ndyns = dyns_sz / dyn_size;
490	gf->gf_dyns = mdb_zalloc(sizeof (GElf_Dyn) * ndyns, UM_SLEEP);
491	gf->gf_ndyns = ndyns;
492
493	dp = dyns;
494	gdp = gf->gf_dyns;
495
496	if (gf->gf_ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
497		for (i = 0; i < ndyns; i++, dp += dyn_size, gdp++) {
498			/* LINTED - alignment */
499			(void) gelf32_to_dyn((Elf32_Dyn *)dp, gdp);
500		}
501	} else {
502		for (i = 0; i < ndyns; i++, dp += dyn_size, gdp++) {
503			/* LINTED - alignment */
504			(void) gelf64_to_dyn((Elf64_Dyn *)dp, gdp);
505		}
506	}
507}
508
509static GElf_Dyn *
510gelf_dyns_init(mdb_gelf_file_t *gf, size_t dyn_size,
511    GElf_Dyn *(*elf2gelf)(const void *, GElf_Dyn *))
512{
513	size_t nbytes, ndyns, i;
514	caddr_t dyns, dp;
515	GElf_Dyn *gdp;
516
517	off64_t dyn_addr;
518
519	if (gf->gf_dyns != NULL)
520		return (gf->gf_dyns);	/* Already loaded */
521
522	if (gf->gf_dynp == NULL)
523		return (NULL);		/* No PT_DYNAMIC entry was found */
524
525	nbytes = gf->gf_dynp->p_filesz;
526	ndyns = nbytes / dyn_size;
527
528	/*
529	 * If this is an executable in PROGRAM view, then p_vaddr is an
530	 * absolute address; we need to subtract the virtual base address of
531	 * the mapping.  In FILE view, dyn_addr is just the file offset.
532	 */
533	if (gf->gf_mode == GF_PROGRAM) {
534		if (gf->gf_ehdr.e_type == ET_EXEC && gf->gf_npload != 0)
535			dyn_addr = gf->gf_dynp->p_vaddr - gf->gf_phdrs->p_vaddr;
536		else
537			dyn_addr = gf->gf_dynp->p_vaddr;
538	} else {
539		mdb_gelf_sect_t *gsp = gf->gf_sects;
540
541		for (i = 0; i < gf->gf_shnum; i++, gsp++) {
542			if (gsp->gs_shdr.sh_type == SHT_DYNAMIC) {
543				dyn_addr = gsp->gs_shdr.sh_offset;
544				break;
545			}
546		}
547
548		if (i == gf->gf_shnum)
549			return (NULL); /* No SHT_DYNAMIC entry was found */
550	}
551
552	mdb_dprintf(MDB_DBG_ELF, "loading _DYNAMIC[] (%lu entries) "
553	    "from offset %llx\n", (ulong_t)ndyns, (longlong_t)dyn_addr);
554
555	if (IOP_SEEK(gf->gf_io, dyn_addr, SEEK_SET) == -1) {
556		warn("failed to seek %s to _DYNAMIC", IOP_NAME(gf->gf_io));
557		return (NULL);
558	}
559
560	dyns = mdb_alloc(nbytes, UM_SLEEP);
561
562	if (IOP_READ(gf->gf_io, dyns, nbytes) != nbytes) {
563		warn("failed to read %s:_DYNAMIC", IOP_NAME(gf->gf_io));
564		mdb_free(dyns, nbytes);
565		return (NULL);
566	}
567
568	gf->gf_dyns = mdb_zalloc(sizeof (GElf_Dyn) * ndyns, UM_SLEEP);
569	gf->gf_ndyns = ndyns;
570
571	dp = dyns;
572	gdp = gf->gf_dyns;
573
574	for (i = 0; i < ndyns; i++, dp += dyn_size, gdp++)
575		(void) elf2gelf(dp, gdp);
576
577	mdb_free(dyns, nbytes);
578	return (gf->gf_dyns);
579}
580
581static mdb_gelf_file_t *
582gelf32_init(mdb_gelf_file_t *gf, mdb_io_t *io, const Elf32_Ehdr *ehdr)
583{
584	/*
585	 * Convert the Elf32_Ehdr to a GElf_Ehdr
586	 */
587	bcopy(ehdr->e_ident, gf->gf_ehdr.e_ident, EI_NIDENT);
588
589	gf->gf_ehdr.e_type = ehdr->e_type;
590	gf->gf_ehdr.e_machine = ehdr->e_machine;
591	gf->gf_ehdr.e_version = ehdr->e_version;
592	gf->gf_ehdr.e_entry = ehdr->e_entry;
593	gf->gf_ehdr.e_phoff = ehdr->e_phoff;
594	gf->gf_ehdr.e_shoff = ehdr->e_shoff;
595	gf->gf_ehdr.e_flags = ehdr->e_flags;
596	gf->gf_ehdr.e_ehsize = ehdr->e_ehsize;
597	gf->gf_ehdr.e_phentsize = ehdr->e_phentsize;
598	gf->gf_ehdr.e_phnum = ehdr->e_phnum;
599	gf->gf_ehdr.e_shentsize = ehdr->e_shentsize;
600	gf->gf_ehdr.e_shnum = ehdr->e_shnum;
601	gf->gf_ehdr.e_shstrndx = ehdr->e_shstrndx;
602
603	gf->gf_shnum = gf->gf_ehdr.e_shnum;
604	gf->gf_shstrndx = gf->gf_ehdr.e_shstrndx;
605	gf->gf_phnum = gf->gf_ehdr.e_phnum;
606
607	if ((gf->gf_shnum == 0 && ehdr->e_shoff != 0) ||
608	    gf->gf_shstrndx == SHN_XINDEX || gf->gf_phnum == PN_XNUM) {
609		Elf32_Shdr shdr0;
610
611		if (ehdr->e_shoff == 0)
612			return (NULL);
613
614		if (IOP_SEEK(io, (off64_t)ehdr->e_shoff, SEEK_SET) == -1) {
615			warn("failed to seek %s", IOP_NAME(io));
616			return (NULL);
617		}
618
619		if (IOP_READ(io, &shdr0, sizeof (shdr0)) != sizeof (shdr0)) {
620			warn("failed to read extended ELF header from %s",
621			    IOP_NAME(io));
622			return (NULL);
623		}
624
625		if (gf->gf_shnum == 0)
626			gf->gf_shnum = shdr0.sh_size;
627
628		if (gf->gf_shstrndx == SHN_XINDEX)
629			gf->gf_shstrndx = shdr0.sh_link;
630
631		if (gf->gf_phnum == PN_XNUM)
632			gf->gf_phnum = shdr0.sh_info;
633	}
634
635	/*
636	 * Initialize the section and program headers.  We skip initializing
637	 * the section headers if this is a program image because they are
638	 * not loadable and thus we can't get at them.
639	 */
640	if (gf->gf_mode == GF_FILE && gelf_shdrs_init(gf, sizeof (Elf32_Shdr),
641	    (GElf_Shdr *(*)(const void *, GElf_Shdr *))gelf32_to_shdr) == NULL)
642		return (NULL);
643
644	if (gelf_phdrs_init(gf, sizeof (Elf32_Phdr),
645	    (GElf_Phdr *(*)(const void *, GElf_Phdr *))gelf32_to_phdr) == NULL)
646		return (NULL);
647
648	(void) gelf_dyns_init(gf, sizeof (Elf32_Dyn),
649	    (GElf_Dyn *(*)(const void *, GElf_Dyn *))gelf32_to_dyn);
650
651	return (gf);
652}
653
654static mdb_gelf_file_t *
655gelf64_init(mdb_gelf_file_t *gf, mdb_io_t *io, Elf64_Ehdr *ehdr)
656{
657	/*
658	 * Save a copy of the ELF file header
659	 */
660	bcopy(ehdr, &gf->gf_ehdr, sizeof (Elf64_Ehdr));
661
662	gf->gf_shnum = gf->gf_ehdr.e_shnum;
663	gf->gf_shstrndx = gf->gf_ehdr.e_shstrndx;
664	gf->gf_phnum = gf->gf_ehdr.e_phnum;
665
666	if ((gf->gf_shnum == 0 && ehdr->e_shoff != 0) ||
667	    gf->gf_shstrndx == SHN_XINDEX || gf->gf_phnum == PN_XNUM) {
668		Elf64_Shdr shdr0;
669
670		if (ehdr->e_shoff == 0)
671			return (NULL);
672
673		if (IOP_SEEK(io, (off64_t)ehdr->e_shoff, SEEK_SET) == -1) {
674			warn("failed to seek %s", IOP_NAME(io));
675			return (NULL);
676		}
677
678		if (IOP_READ(io, &shdr0, sizeof (shdr0)) != sizeof (shdr0)) {
679			warn("failed to read extended ELF header from %s",
680			    IOP_NAME(io));
681			return (NULL);
682		}
683
684		if (gf->gf_shnum == 0)
685			gf->gf_shnum = shdr0.sh_size;
686
687		if (gf->gf_shstrndx == SHN_XINDEX)
688			gf->gf_shstrndx = shdr0.sh_link;
689
690		if (gf->gf_phnum == PN_XNUM)
691			gf->gf_phnum = shdr0.sh_info;
692	}
693
694	/*
695	 * Initialize the section and program headers.  We skip initializing
696	 * the section headers if this is a program image because they are
697	 * not loadable and thus we can't get at them.
698	 */
699	if (gf->gf_mode == GF_FILE && gelf_shdrs_init(gf, sizeof (Elf64_Shdr),
700	    (GElf_Shdr *(*)(const void *, GElf_Shdr *))gelf64_to_shdr) == NULL)
701		return (NULL);
702
703	if (gelf_phdrs_init(gf, sizeof (Elf64_Phdr),
704	    (GElf_Phdr *(*)(const void *, GElf_Phdr *))gelf64_to_phdr) == NULL)
705		return (NULL);
706
707	(void) gelf_dyns_init(gf, sizeof (Elf64_Dyn),
708	    (GElf_Dyn *(*)(const void *, GElf_Dyn *))gelf64_to_dyn);
709
710	return (gf);
711}
712
713int
714mdb_gelf_check(mdb_io_t *io, Elf32_Ehdr *ehp, GElf_Half etype)
715{
716#ifdef _BIG_ENDIAN
717	uchar_t order = ELFDATA2MSB;
718#else
719	uchar_t order = ELFDATA2LSB;
720#endif
721	ssize_t nbytes;
722
723	(void) IOP_SEEK(io, (off64_t)0L, SEEK_SET);
724	nbytes = IOP_READ(io, ehp, sizeof (Elf32_Ehdr));
725
726	if (nbytes == -1) {
727		if (etype != ET_NONE)
728			warn("failed to read ELF header from %s", IOP_NAME(io));
729		return (-1);
730	}
731
732	if (nbytes != sizeof (Elf32_Ehdr) ||
733	    bcmp(&ehp->e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0) {
734		if (etype != ET_NONE)
735			warn("%s is not an ELF file\n", IOP_NAME(io));
736		return (-1);
737	}
738
739	if (ehp->e_ident[EI_DATA] != order) {
740		warn("ELF file %s has different endianness from debugger\n",
741		    IOP_NAME(io));
742		return (-1);
743	}
744
745	if (ehp->e_version != EV_CURRENT) {
746		warn("ELF file %s uses different ELF version (%lu) than "
747		    "debugger (%u)\n", IOP_NAME(io),
748		    (ulong_t)ehp->e_version, EV_CURRENT);
749		return (-1);
750	}
751
752	if (etype != ET_NONE && ehp->e_type != etype) {
753		warn("ELF file %s is not of the expected type\n", IOP_NAME(io));
754		return (-1);
755	}
756
757	return (0);
758}
759
760mdb_gelf_file_t *
761mdb_gelf_create(mdb_io_t *io, GElf_Half etype, int mode)
762{
763	union {
764		Elf32_Ehdr h32;
765		Elf64_Ehdr h64;
766	} ehdr;
767
768	mdb_gelf_file_t *gf = mdb_zalloc(sizeof (mdb_gelf_file_t), UM_SLEEP);
769
770	ASSERT(mode == GF_FILE || mode == GF_PROGRAM);
771	gf->gf_mode = mode;
772
773	/*
774	 * Assign the i/o backend now, but don't hold it until we're sure
775	 * we're going to succeed; otherwise the caller will be responsible
776	 * for mdb_io_destroy()ing it.
777	 */
778	gf->gf_io = io;
779
780	if (mdb_gelf_check(io, &ehdr.h32, etype) == -1)
781		goto err;
782
783	switch (ehdr.h32.e_ident[EI_CLASS]) {
784	case ELFCLASS32:
785		gf = gelf32_init(gf, io, &ehdr.h32);
786		break;
787
788	case ELFCLASS64:
789		if (IOP_SEEK(io, (off64_t)0L, SEEK_SET) == -1) {
790			warn("failed to seek %s", IOP_NAME(io));
791			goto err;
792		}
793
794		if (IOP_READ(io, &ehdr.h64, sizeof (ehdr.h64)) !=
795		    sizeof (ehdr.h64)) {
796			warn("failed to read ELF header from %s", IOP_NAME(io));
797			goto err;
798		}
799
800		gf = gelf64_init(gf, io, &ehdr.h64);
801		break;
802
803	default:
804		warn("%s is an unsupported ELF class: %u\n",
805		    IOP_NAME(io), ehdr.h32.e_ident[EI_CLASS]);
806		goto err;
807	}
808
809	if (gf != NULL && gelf_sect_init(gf) != NULL) {
810		gf->gf_io = mdb_io_hold(io);
811		return (gf);
812	}
813
814err:
815	if (gf != NULL) {
816		if (gf->gf_sects != NULL) {
817			mdb_free(gf->gf_sects, gf->gf_shnum *
818			    sizeof (mdb_gelf_sect_t));
819		}
820		mdb_free(gf, sizeof (mdb_gelf_file_t));
821	}
822	return (NULL);
823}
824
825void
826mdb_gelf_destroy(mdb_gelf_file_t *gf)
827{
828	mdb_gelf_sect_t *gsp;
829	size_t i;
830
831	for (gsp = gf->gf_sects, i = 0; i < gf->gf_shnum; i++, gsp++) {
832		if (gsp->gs_data != NULL)
833			mdb_free(gsp->gs_data, gsp->gs_shdr.sh_size);
834	}
835
836	mdb_free(gf->gf_sects,
837	    gf->gf_shnum * sizeof (mdb_gelf_sect_t));
838
839	mdb_free(gf->gf_phdrs, gf->gf_phnum * sizeof (GElf_Phdr));
840
841	mdb_io_rele(gf->gf_io);
842	mdb_free(gf, sizeof (mdb_gelf_file_t));
843}
844
845/*
846 * Sort comparison function for 32-bit symbol address-to-name lookups.  We sort
847 * symbols by value.  If values are equal, we prefer the symbol that is
848 * non-zero sized, typed, not weak, or lexically first, in that order.
849 */
850static int
851gelf32_sym_compare(const void *lp, const void *rp)
852{
853	Elf32_Sym *lhs = *((Elf32_Sym **)lp);
854	Elf32_Sym *rhs = *((Elf32_Sym **)rp);
855
856	if (lhs->st_value != rhs->st_value)
857		return (lhs->st_value > rhs->st_value ? 1 : -1);
858
859	if ((lhs->st_size == 0) != (rhs->st_size == 0))
860		return (lhs->st_size == 0 ? 1 : -1);
861
862	if ((ELF32_ST_TYPE(lhs->st_info) == STT_NOTYPE) !=
863	    (ELF32_ST_TYPE(rhs->st_info) == STT_NOTYPE))
864		return (ELF32_ST_TYPE(lhs->st_info) == STT_NOTYPE ? 1 : -1);
865
866	if ((ELF32_ST_BIND(lhs->st_info) == STB_WEAK) !=
867	    (ELF32_ST_BIND(rhs->st_info) == STB_WEAK))
868		return (ELF32_ST_BIND(lhs->st_info) == STB_WEAK ? 1 : -1);
869
870	return (strcmp(gelf_strtab + lhs->st_name, gelf_strtab + rhs->st_name));
871}
872
873/*
874 * Sort comparison function for 64-bit symbol address-to-name lookups.  We sort
875 * symbols by value.  If values are equal, we prefer the symbol that is
876 * non-zero sized, typed, not weak, or lexically first, in that order.
877 */
878static int
879gelf64_sym_compare(const void *lp, const void *rp)
880{
881	Elf64_Sym *lhs = *((Elf64_Sym **)lp);
882	Elf64_Sym *rhs = *((Elf64_Sym **)rp);
883
884	if (lhs->st_value != rhs->st_value)
885		return (lhs->st_value > rhs->st_value ? 1 : -1);
886
887	if ((lhs->st_size == 0) != (rhs->st_size == 0))
888		return (lhs->st_size == 0 ? 1 : -1);
889
890	if ((ELF64_ST_TYPE(lhs->st_info) == STT_NOTYPE) !=
891	    (ELF64_ST_TYPE(rhs->st_info) == STT_NOTYPE))
892		return (ELF64_ST_TYPE(lhs->st_info) == STT_NOTYPE ? 1 : -1);
893
894	if ((ELF64_ST_BIND(lhs->st_info) == STB_WEAK) !=
895	    (ELF64_ST_BIND(rhs->st_info) == STB_WEAK))
896		return (ELF64_ST_BIND(lhs->st_info) == STB_WEAK ? 1 : -1);
897
898	return (strcmp(gelf_strtab + lhs->st_name, gelf_strtab + rhs->st_name));
899}
900
901static void
902gelf32_symtab_sort(mdb_gelf_symtab_t *gst)
903{
904	Elf32_Sym **sympp = (Elf32_Sym **)gst->gst_asmap;
905	mdb_var_t *v;
906
907	mdb_nv_rewind(&gst->gst_nv);
908
909	while ((v = mdb_nv_advance(&gst->gst_nv)) != NULL) {
910		Elf32_Sym *sym = MDB_NV_COOKIE(v);
911		if (sym->st_value != 0 &&
912		    (ELF32_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size))
913			*sympp++ = sym;
914	}
915
916	gst->gst_aslen = (size_t)(sympp - (Elf32_Sym **)gst->gst_asmap);
917	ASSERT(gst->gst_aslen <= gst->gst_asrsv);
918
919	gelf_strtab = gst->gst_ssect ? gst->gst_ssect->gs_data : NULL;
920
921	qsort(gst->gst_asmap, gst->gst_aslen,
922	    sizeof (Elf32_Sym *), gelf32_sym_compare);
923
924	gelf_strtab = NULL;
925}
926
927static void
928gelf32_symtab_init(mdb_gelf_symtab_t *gst)
929{
930#if STT_NUM != (STT_TLS + 1)
931#error "STT_NUM has grown. update gelf32_symtab_init()"
932#endif
933
934	const char *base = (const char *)gst->gst_ssect->gs_data;
935	Elf32_Sym *sym = gst->gst_dsect->gs_data;
936	mdb_nv_t *nv = &gst->gst_nv;
937
938	Elf32_Word ss_size = gst->gst_ssect->gs_shdr.sh_size;
939	size_t asrsv = 0;
940	GElf_Word i, n;
941
942	if (gst->gst_dsect->gs_shdr.sh_entsize != sizeof (Elf32_Sym)) {
943		warn("%s sh_entsize %llu != sizeof (Elf32_Sym); "
944		    "using %u instead\n", gst->gst_dsect->gs_name,
945		    (u_longlong_t)gst->gst_dsect->gs_shdr.sh_entsize,
946		    (uint_t)sizeof (Elf32_Sym));
947		gst->gst_dsect->gs_shdr.sh_entsize = sizeof (Elf32_Sym);
948	}
949
950	n = gst->gst_dsect->gs_shdr.sh_size /
951	    gst->gst_dsect->gs_shdr.sh_entsize;
952
953	for (i = 0; i < n; i++, sym++) {
954		const char *name = base + sym->st_name;
955		uchar_t type = ELF32_ST_TYPE(sym->st_info);
956
957		if (type >= STT_NUM || type == STT_SECTION)
958			continue; /* skip sections and unknown types */
959
960		if (sym->st_name >= ss_size || name[0] < '!' || name[0] > '~') {
961			if (sym->st_name >= ss_size || name[0] != '\0') {
962				warn("ignoring %s symbol [%u]: invalid name\n",
963				    gst->gst_dsect->gs_name, i);
964				sym->st_name = 0;
965			}
966			continue; /* skip corrupt or empty names */
967		}
968
969		(void) mdb_nv_insert(nv, name, NULL, (uintptr_t)sym, GST_NVFLG);
970
971		if (sym->st_value != 0 &&
972		    (ELF32_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size))
973			asrsv++; /* reserve space in the address map */
974	}
975
976	if (gst->gst_ehdr->e_type == ET_REL && gst->gst_file != NULL) {
977		GElf_Word smax = gst->gst_file->gf_shnum;
978		mdb_gelf_sect_t *gsp;
979
980		for (sym = gst->gst_dsect->gs_data, i = 0; i < n; i++, sym++) {
981			if (sym->st_shndx > SHN_UNDEF && sym->st_shndx < smax) {
982				gsp = &gst->gst_file->gf_sects[sym->st_shndx];
983				sym->st_value += gsp->gs_shdr.sh_offset;
984
985				if (ELF32_ST_BIND(sym->st_info) != STB_LOCAL ||
986				    sym->st_size != 0)
987					asrsv++; /* reserve space in asmap */
988			}
989		}
990	}
991
992	gst->gst_asmap = mdb_alloc(sizeof (Elf32_Sym *) * asrsv, UM_SLEEP);
993	gst->gst_asrsv = asrsv;
994
995	gelf32_symtab_sort(gst);
996}
997
998static void
999gelf64_symtab_sort(mdb_gelf_symtab_t *gst)
1000{
1001	Elf64_Sym **sympp = (Elf64_Sym **)gst->gst_asmap;
1002	mdb_var_t *v;
1003
1004	mdb_nv_rewind(&gst->gst_nv);
1005
1006	while ((v = mdb_nv_advance(&gst->gst_nv)) != NULL) {
1007		Elf64_Sym *sym = MDB_NV_COOKIE(v);
1008		if (sym->st_value != 0 &&
1009		    (ELF64_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size))
1010			*sympp++ = sym;
1011	}
1012
1013	gst->gst_aslen = (size_t)(sympp - (Elf64_Sym **)gst->gst_asmap);
1014	ASSERT(gst->gst_aslen <= gst->gst_asrsv);
1015
1016	gelf_strtab = gst->gst_ssect ? gst->gst_ssect->gs_data : NULL;
1017
1018	qsort(gst->gst_asmap, gst->gst_aslen,
1019	    sizeof (Elf64_Sym *), gelf64_sym_compare);
1020
1021	gelf_strtab = NULL;
1022}
1023
1024static void
1025gelf64_symtab_init(mdb_gelf_symtab_t *gst)
1026{
1027#if STT_NUM != (STT_TLS + 1)
1028#error "STT_NUM has grown. update gelf64_symtab_init()"
1029#endif
1030
1031	const char *base = (const char *)gst->gst_ssect->gs_data;
1032	Elf64_Sym *sym = gst->gst_dsect->gs_data;
1033	mdb_nv_t *nv = &gst->gst_nv;
1034
1035	Elf64_Xword ss_size = gst->gst_ssect->gs_shdr.sh_size;
1036	size_t asrsv = 0;
1037	GElf_Word i, n;
1038
1039	if (gst->gst_dsect->gs_shdr.sh_entsize != sizeof (Elf64_Sym)) {
1040		warn("%s sh_entsize %llu != sizeof (Elf64_Sym); "
1041		    "using %u instead\n", gst->gst_dsect->gs_name,
1042		    (u_longlong_t)gst->gst_dsect->gs_shdr.sh_entsize,
1043		    (uint_t)sizeof (Elf64_Sym));
1044		gst->gst_dsect->gs_shdr.sh_entsize = sizeof (Elf64_Sym);
1045	}
1046
1047	n = gst->gst_dsect->gs_shdr.sh_size /
1048	    gst->gst_dsect->gs_shdr.sh_entsize;
1049
1050	for (i = 0; i < n; i++, sym++) {
1051		const char *name = base + sym->st_name;
1052		uchar_t type = ELF64_ST_TYPE(sym->st_info);
1053
1054		if (type >= STT_NUM || type == STT_SECTION)
1055			continue; /* skip sections and unknown types */
1056
1057		if (sym->st_name >= ss_size || name[0] < '!' || name[0] > '~') {
1058			if (sym->st_name >= ss_size || name[0] != '\0') {
1059				warn("ignoring %s symbol [%u]: invalid name\n",
1060				    gst->gst_dsect->gs_name, i);
1061				sym->st_name = 0;
1062			}
1063			continue; /* skip corrupt or empty names */
1064		}
1065
1066		(void) mdb_nv_insert(nv, name, NULL, (uintptr_t)sym, GST_NVFLG);
1067
1068		if (sym->st_value != 0 &&
1069		    (ELF64_ST_BIND(sym->st_info) != STB_LOCAL || sym->st_size))
1070			asrsv++; /* reserve space in the address map */
1071	}
1072
1073	if (gst->gst_ehdr->e_type == ET_REL && gst->gst_file != NULL) {
1074		GElf_Word smax = gst->gst_file->gf_shnum;
1075		mdb_gelf_sect_t *gsp;
1076
1077		for (sym = gst->gst_dsect->gs_data, i = 0; i < n; i++, sym++) {
1078			if (sym->st_shndx > SHN_UNDEF && sym->st_shndx < smax) {
1079				gsp = &gst->gst_file->gf_sects[sym->st_shndx];
1080				sym->st_value += gsp->gs_shdr.sh_offset;
1081
1082				if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL ||
1083				    sym->st_size != 0)
1084					asrsv++; /* reserve space in asmap */
1085			}
1086		}
1087	}
1088
1089	gst->gst_asmap = mdb_alloc(sizeof (Elf64_Sym *) * asrsv, UM_SLEEP);
1090	gst->gst_asrsv = asrsv;
1091
1092	gelf64_symtab_sort(gst);
1093}
1094
1095mdb_gelf_symtab_t *
1096mdb_gelf_symtab_create_file(mdb_gelf_file_t *gf, GElf_Word elftype,
1097    uint_t tabid)
1098{
1099	mdb_gelf_sect_t *gsp;
1100	const char *dsname = NULL;
1101	const char *ssname;
1102	size_t i;
1103	GElf_Word link;
1104
1105	/*
1106	 * Examine the sh_link field in the the Elf header to get the name
1107	 * of the corresponding strings section
1108	 */
1109	for (gsp = gf->gf_sects, i = 0; i < gf->gf_shnum; i++, gsp++) {
1110		if (gsp->gs_shdr.sh_type == elftype) {
1111			dsname = gsp->gs_name;
1112			link = gsp->gs_shdr.sh_link;
1113			break;
1114		}
1115	}
1116
1117	if (dsname == NULL)
1118		return (NULL);
1119
1120	if (link > gf->gf_shnum) {
1121		/*
1122		 * Invalid link number due to corrupt elf file.
1123		 */
1124		warn("link number %ud larger than number of sections %d\n",
1125		    link, gf->gf_shnum);
1126		return (NULL);
1127	}
1128
1129	ssname = (gf->gf_sects + link)->gs_name;
1130
1131	return (mdb_gelf_symtab_create_file_by_name(gf, dsname, ssname, tabid));
1132}
1133
1134mdb_gelf_symtab_t *
1135mdb_gelf_symtab_create_file_by_name(mdb_gelf_file_t *gf,
1136    const char *dsname, const char *ssname, uint_t tabid)
1137{
1138	mdb_gelf_symtab_t *gst;
1139	mdb_gelf_sect_t *gsp;
1140	size_t i;
1141
1142	gst = mdb_alloc(sizeof (mdb_gelf_symtab_t), UM_SLEEP);
1143	(void) mdb_nv_create(&gst->gst_nv, UM_SLEEP);
1144
1145	gst->gst_asmap = NULL;
1146	gst->gst_aslen = 0;
1147	gst->gst_asrsv = 0;
1148	gst->gst_ehdr = &gf->gf_ehdr;
1149	gst->gst_file = gf;
1150	gst->gst_dsect = NULL;
1151	gst->gst_ssect = NULL;
1152	gst->gst_id = 0;
1153	gst->gst_tabid = tabid;
1154
1155	for (gsp = gf->gf_sects, i = 0; i < gf->gf_shnum; i++, gsp++) {
1156		if (strcmp(gsp->gs_name, dsname) == 0) {
1157			gst->gst_dsect = gsp;
1158			break;
1159		}
1160	}
1161
1162	for (gsp = gf->gf_sects, i = 0; i < gf->gf_shnum; i++, gsp++) {
1163		if (strcmp(gsp->gs_name, ssname) == 0) {
1164			gst->gst_ssect = gsp;
1165			break;
1166		}
1167	}
1168
1169	if (gst->gst_dsect == NULL || gst->gst_ssect == NULL)
1170		goto err; /* Failed to locate data or string section */
1171
1172	if (mdb_gelf_sect_load(gf, gst->gst_dsect) == NULL)
1173		goto err; /* Failed to load data section */
1174
1175	if (mdb_gelf_sect_load(gf, gst->gst_ssect) == NULL)
1176		goto err; /* Failed to load string section */
1177
1178	if (gf->gf_ehdr.e_ident[EI_CLASS] == ELFCLASS32)
1179		gelf32_symtab_init(gst);
1180	else
1181		gelf64_symtab_init(gst);
1182
1183	return (gst);
1184
1185err:
1186	mdb_nv_destroy(&gst->gst_nv);
1187	mdb_free(gst, sizeof (mdb_gelf_symtab_t));
1188	return (NULL);
1189}
1190
1191mdb_gelf_symtab_t *
1192mdb_gelf_symtab_create_raw(const GElf_Ehdr *ehdr, const void *dshdr,
1193    void *ddata, const void *sshdr, void *sdata, uint_t tabid)
1194{
1195	mdb_gelf_symtab_t *gst;
1196
1197	gst = mdb_alloc(sizeof (mdb_gelf_symtab_t), UM_SLEEP);
1198	(void) mdb_nv_create(&gst->gst_nv, UM_SLEEP);
1199
1200	gst->gst_asmap = NULL;
1201	gst->gst_aslen = 0;
1202	gst->gst_asrsv = 0;
1203	gst->gst_ehdr = ehdr;
1204	gst->gst_file = NULL; /* Flag for raw symtab */
1205	gst->gst_id = 0;
1206	gst->gst_tabid = tabid;
1207
1208	gst->gst_dsect = mdb_zalloc(sizeof (mdb_gelf_sect_t), UM_SLEEP);
1209	gst->gst_dsect->gs_name = ".symtab";
1210	gst->gst_dsect->gs_data = ddata;
1211
1212	gst->gst_ssect = mdb_zalloc(sizeof (mdb_gelf_sect_t), UM_SLEEP);
1213	gst->gst_ssect->gs_name = ".strtab";
1214	gst->gst_ssect->gs_data = sdata;
1215
1216	if (ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
1217		(void) gelf32_to_shdr(dshdr, &gst->gst_dsect->gs_shdr);
1218		(void) gelf32_to_shdr(sshdr, &gst->gst_ssect->gs_shdr);
1219		gelf32_symtab_init(gst);
1220	} else {
1221		(void) gelf64_to_shdr(dshdr, &gst->gst_dsect->gs_shdr);
1222		(void) gelf64_to_shdr(sshdr, &gst->gst_ssect->gs_shdr);
1223		gelf64_symtab_init(gst);
1224	}
1225
1226	return (gst);
1227}
1228
1229mdb_gelf_symtab_t *
1230mdb_gelf_symtab_create_dynamic(mdb_gelf_file_t *gf, uint_t tabid)
1231{
1232	GElf_Addr dt_symtab, dt_strtab, dt_hash;
1233	GElf_Xword dt_syment, dt_strsz;
1234
1235	mdb_gelf_symtab_t *gst;
1236	uint_t hash_h[2];
1237	off64_t base = 0;
1238
1239	ASSERT(gf->gf_mode == GF_PROGRAM);
1240
1241	/*
1242	 * Read in and cache the array of GElf_Dyn structures from the
1243	 * PT_DYNAMIC phdr.  Abort if this is not possible.
1244	 */
1245	if (gf->gf_ehdr.e_ident[EI_CLASS] == ELFCLASS32) {
1246		(void) gelf_dyns_init(gf, sizeof (Elf32_Dyn),
1247		    (GElf_Dyn *(*)(const void *, GElf_Dyn *))gelf32_to_dyn);
1248	} else {
1249		(void) gelf_dyns_init(gf, sizeof (Elf64_Dyn),
1250		    (GElf_Dyn *(*)(const void *, GElf_Dyn *))gelf64_to_dyn);
1251	}
1252
1253	/*
1254	 * Pre-fetch all the DT_* entries we will need for creating the
1255	 * dynamic symbol table; abort if any are missing.
1256	 */
1257	if ((dt_hash = gelf_dyn_lookup(gf, DT_HASH)) == -1L) {
1258		warn("failed to get DT_HASH for %s\n", IOP_NAME(gf->gf_io));
1259		return (NULL);
1260	}
1261
1262	if ((dt_symtab = gelf_dyn_lookup(gf, DT_SYMTAB)) == -1L) {
1263		warn("failed to get DT_SYMTAB for %s\n", IOP_NAME(gf->gf_io));
1264		return (NULL);
1265	}
1266
1267	if ((dt_syment = gelf_dyn_lookup(gf, DT_SYMENT)) == -1L) {
1268		warn("failed to get DT_SYMENT for %s\n", IOP_NAME(gf->gf_io));
1269		return (NULL);
1270	}
1271
1272	if ((dt_strtab = gelf_dyn_lookup(gf, DT_STRTAB)) == -1L) {
1273		warn("failed to get DT_STRTAB for %s\n", IOP_NAME(gf->gf_io));
1274		return (NULL);
1275	}
1276
1277	if ((dt_strsz = gelf_dyn_lookup(gf, DT_STRSZ)) == -1L) {
1278		warn("failed to get DT_STRSZ for %s\n", IOP_NAME(gf->gf_io));
1279		return (NULL);
1280	}
1281
1282	/*
1283	 * If this is an executable, then DT_HASH is an absolute address;
1284	 * we need to subtract the virtual base address of the mapping.
1285	 */
1286	if (gf->gf_ehdr.e_type == ET_EXEC && gf->gf_npload != 0)
1287		base = (off64_t)gf->gf_phdrs->p_vaddr;
1288
1289	/*
1290	 * Read in the header for the DT_HASH: this consists of nbucket
1291	 * and nchain values (nchain is the number of hashed symbols).
1292	 */
1293	if (IOP_SEEK(gf->gf_io, (off64_t)dt_hash - base, SEEK_SET) == -1) {
1294		warn("failed to seek ELF file to start of DT_HASH");
1295		return (NULL);
1296	}
1297
1298	if (IOP_READ(gf->gf_io, hash_h, sizeof (hash_h)) != sizeof (hash_h)) {
1299		warn("failed to read DT_HASH header");
1300		return (NULL);
1301	}
1302
1303	gst = mdb_zalloc(sizeof (mdb_gelf_symtab_t), UM_SLEEP);
1304	(void) mdb_nv_create(&gst->gst_nv, UM_SLEEP);
1305
1306	gst->gst_asmap = NULL;
1307	gst->gst_aslen = 0;
1308	gst->gst_asrsv = 0;
1309	gst->gst_ehdr = &gf->gf_ehdr;
1310	gst->gst_file = gf;
1311	gst->gst_id = 0;
1312	gst->gst_tabid = tabid;
1313
1314	gst->gst_dsect = mdb_zalloc(sizeof (mdb_gelf_sect_t), UM_SLEEP);
1315	gst->gst_dsect->gs_name = ".dynsym";
1316	gst->gst_dsect->gs_shdr.sh_offset = dt_symtab - (GElf_Addr)base;
1317	gst->gst_dsect->gs_shdr.sh_size = hash_h[1] * dt_syment;
1318	gst->gst_dsect->gs_shdr.sh_entsize = dt_syment;
1319
1320	gst->gst_ssect = mdb_zalloc(sizeof (mdb_gelf_sect_t), UM_SLEEP);
1321	gst->gst_ssect->gs_name = ".dynstr";
1322	gst->gst_ssect->gs_shdr.sh_offset = dt_strtab - (GElf_Addr)base;
1323	gst->gst_ssect->gs_shdr.sh_size = dt_strsz;
1324	gst->gst_ssect->gs_shdr.sh_entsize = 0;
1325
1326	if (mdb_gelf_sect_load(gf, gst->gst_dsect) == NULL)
1327		goto err;
1328
1329	if (mdb_gelf_sect_load(gf, gst->gst_ssect) == NULL)
1330		goto err;
1331
1332	if (gf->gf_ehdr.e_ident[EI_CLASS] == ELFCLASS32)
1333		gelf32_symtab_init(gst);
1334	else
1335		gelf64_symtab_init(gst);
1336
1337	return (gst);
1338
1339err:
1340	mdb_gelf_symtab_destroy(gst);
1341	return (NULL);
1342}
1343
1344mdb_gelf_symtab_t *
1345mdb_gelf_symtab_create_mutable(void)
1346{
1347	mdb_gelf_symtab_t *gst;
1348	static GElf_Ehdr ehdr;
1349
1350	gst = mdb_zalloc(sizeof (mdb_gelf_symtab_t), UM_SLEEP);
1351	(void) mdb_nv_create(&gst->gst_nv, UM_SLEEP);
1352	gst->gst_ehdr = &ehdr;
1353
1354	if (ehdr.e_version == 0) {
1355#ifdef	_LP64
1356		uchar_t class = ELFCLASS64;
1357#else
1358		uchar_t class = ELFCLASS32;
1359#endif
1360
1361#ifdef _BIG_ENDIAN
1362		uchar_t data = ELFDATA2MSB;
1363#else
1364		uchar_t data = ELFDATA2LSB;
1365#endif
1366		/*
1367		 * Since all mutable symbol tables will use a native Ehdr,
1368		 * we can just have a single static copy which they all
1369		 * point to and we only need initialize once.
1370		 */
1371		ehdr.e_ident[EI_MAG0] = ELFMAG0;
1372		ehdr.e_ident[EI_MAG1] = ELFMAG1;
1373		ehdr.e_ident[EI_MAG2] = ELFMAG2;
1374		ehdr.e_ident[EI_MAG3] = ELFMAG3;
1375		ehdr.e_ident[EI_CLASS] = class;
1376		ehdr.e_ident[EI_DATA] = data;
1377		ehdr.e_ident[EI_VERSION] = EV_CURRENT;
1378		ehdr.e_type = ET_NONE;
1379		ehdr.e_version = EV_CURRENT;
1380	}
1381
1382	return (gst);
1383}
1384
1385void
1386mdb_gelf_symtab_destroy(mdb_gelf_symtab_t *gst)
1387{
1388	if (gst->gst_file == NULL) {
1389		if (gst->gst_dsect == NULL && gst->gst_ssect == NULL) {
1390			mdb_var_t *v;
1391
1392			mdb_nv_rewind(&gst->gst_nv);
1393			while ((v = mdb_nv_advance(&gst->gst_nv)) != NULL) {
1394				char *name = (char *)mdb_nv_get_name(v);
1395				mdb_gelf_dsym_t *dsp = mdb_nv_get_cookie(v);
1396
1397				mdb_free(name, strlen(name) + 1);
1398				mdb_free(dsp, sizeof (mdb_gelf_dsym_t));
1399			}
1400
1401		} else {
1402			mdb_free(gst->gst_dsect, sizeof (mdb_gelf_sect_t));
1403			mdb_free(gst->gst_ssect, sizeof (mdb_gelf_sect_t));
1404		}
1405
1406	} else if (gst->gst_file->gf_mode == GF_PROGRAM) {
1407		mdb_gelf_sect_t *dsect = gst->gst_dsect;
1408		mdb_gelf_sect_t *ssect = gst->gst_ssect;
1409
1410		if (dsect->gs_data != NULL)
1411			mdb_free(dsect->gs_data, dsect->gs_shdr.sh_size);
1412		if (ssect->gs_data != NULL)
1413			mdb_free(ssect->gs_data, ssect->gs_shdr.sh_size);
1414
1415		mdb_free(gst->gst_dsect, sizeof (mdb_gelf_sect_t));
1416		mdb_free(gst->gst_ssect, sizeof (mdb_gelf_sect_t));
1417	}
1418
1419	mdb_nv_destroy(&gst->gst_nv);
1420	mdb_free(gst->gst_asmap, gst->gst_asrsv * sizeof (void *));
1421	mdb_free(gst, sizeof (mdb_gelf_symtab_t));
1422}
1423
1424size_t
1425mdb_gelf_symtab_size(mdb_gelf_symtab_t *gst)
1426{
1427	return (mdb_nv_size(&gst->gst_nv));
1428}
1429
1430static GElf_Sym *
1431gelf32_to_sym(const Elf32_Sym *src, GElf_Sym *dst)
1432{
1433	if (src != NULL) {
1434		dst->st_name = src->st_name;
1435		dst->st_info = src->st_info;
1436		dst->st_other = src->st_other;
1437		dst->st_shndx = src->st_shndx;
1438		dst->st_value = src->st_value;
1439		dst->st_size = src->st_size;
1440		return (dst);
1441	}
1442
1443	return (NULL);
1444}
1445
1446static GElf_Sym *
1447gelf64_to_sym(const Elf64_Sym *src, GElf_Sym *dst)
1448{
1449	if (src != NULL) {
1450		bcopy(src, dst, sizeof (GElf_Sym));
1451		return (dst);
1452	}
1453
1454	return (NULL);
1455}
1456
1457/*ARGSUSED*/
1458static GElf_Sym *
1459gelf64_nocopy(const Elf64_Sym *src, GElf_Sym *dst)
1460{
1461	return ((GElf_Sym *)src);
1462}
1463
1464static const void *
1465gelf32_sym_search(const Elf32_Sym **asmap, size_t aslen, uintptr_t addr)
1466{
1467	ulong_t i, mid, lo = 0, hi = aslen - 1;
1468	const Elf32_Sym *symp;
1469	Elf32_Addr v;
1470	size_t size;
1471
1472	if (aslen == 0)
1473		return (NULL);
1474
1475	while (hi - lo > 1) {
1476		mid = (lo + hi) / 2;
1477		if (addr >= asmap[mid]->st_value)
1478			lo = mid;
1479		else
1480			hi = mid;
1481	}
1482
1483	i = addr < asmap[hi]->st_value ? lo : hi;
1484	symp = asmap[i];
1485	v = symp->st_value;
1486
1487	/*
1488	 * If the previous entry has the same value, improve our choice.  The
1489	 * order of equal-valued symbols is determined by gelf32_sym_compare().
1490	 */
1491	while (i-- != 0 && asmap[i]->st_value == v)
1492		symp = asmap[i];
1493
1494	/*
1495	 * If an absolute symbol distance was specified, use that; otherwise
1496	 * use the ELF symbol size, or 1 byte if the ELF size is zero.
1497	 */
1498	if (mdb.m_symdist == 0)
1499		size = MAX(symp->st_size, 1);
1500	else
1501		size = mdb.m_symdist;
1502
1503	if (addr - symp->st_value < size)
1504		return (symp);
1505
1506	return (NULL);
1507}
1508
1509static const void *
1510gelf64_sym_search(const Elf64_Sym **asmap, size_t aslen, uintptr_t addr)
1511{
1512	ulong_t i, mid, lo = 0, hi = aslen - 1;
1513	const Elf64_Sym *symp;
1514	Elf64_Addr v;
1515	size_t size;
1516
1517	if (aslen == 0)
1518		return (NULL);
1519
1520	while (hi - lo > 1) {
1521		mid = (lo + hi) / 2;
1522		if (addr >= asmap[mid]->st_value)
1523			lo = mid;
1524		else
1525			hi = mid;
1526	}
1527
1528	i = addr < asmap[hi]->st_value ? lo : hi;
1529	symp = asmap[i];
1530	v = symp->st_value;
1531
1532	/*
1533	 * If the previous entry has the same value, improve our choice.  The
1534	 * order of equal-valued symbols is determined by gelf64_sym_compare().
1535	 */
1536	while (i-- != 0 && asmap[i]->st_value == v)
1537		symp = asmap[i];
1538
1539	/*
1540	 * If an absolute symbol distance was specified, use that; otherwise
1541	 * use the ELF symbol size, or 1 byte if the ELF size is zero.
1542	 */
1543	if (mdb.m_symdist == 0)
1544		size = MAX(symp->st_size, 1);
1545	else
1546		size = mdb.m_symdist;
1547
1548	if (addr - symp->st_value < size)
1549		return (symp);
1550
1551	return (NULL);
1552}
1553
1554const char *
1555mdb_gelf_sym_name(mdb_gelf_symtab_t *gst, const GElf_Sym *sym)
1556{
1557	const mdb_gelf_dsym_t *dsp;
1558
1559	if (gst->gst_ssect != NULL)
1560		return ((const char *)gst->gst_ssect->gs_data + sym->st_name);
1561
1562	if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32)
1563		dsp = gelf32_sym_search(gst->gst_asmap,
1564		    gst->gst_aslen, sym->st_value);
1565	else
1566		dsp = gelf64_sym_search(gst->gst_asmap,
1567		    gst->gst_aslen, sym->st_value);
1568
1569	if (dsp != NULL)
1570		return (mdb_nv_get_name(dsp->ds_var));
1571
1572	return (NULL);
1573}
1574
1575int
1576mdb_gelf_sym_closer(const GElf_Sym *s1, const GElf_Sym *s2, uintptr_t addr)
1577{
1578	uintptr_t v1 = (uintptr_t)s1->st_value;
1579	uintptr_t v2 = (uintptr_t)s2->st_value;
1580
1581	uintptr_t d1 = v1 > addr ? v1 - addr : addr - v1;
1582	uintptr_t d2 = v2 > addr ? v2 - addr : addr - v2;
1583
1584	return (d1 < d2);
1585}
1586
1587int
1588mdb_gelf_symtab_lookup_by_addr(mdb_gelf_symtab_t *gst, uintptr_t addr,
1589    uint_t flags, char *buf, size_t nbytes, GElf_Sym *sym, uint_t *idp)
1590{
1591	union {
1592		const mdb_gelf_dsym_t *dsp;
1593		const Elf32_Sym *s32;
1594		const Elf64_Sym *s64;
1595		caddr_t sp;
1596	} u;
1597
1598	const char *name;
1599
1600	if (gst == NULL)
1601		return (set_errno(EMDB_NOSYMADDR));
1602
1603	if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
1604		u.s32 = gelf32_sym_search(gst->gst_asmap, gst->gst_aslen, addr);
1605		if (gelf32_to_sym(u.s32, sym) == NULL)
1606			return (set_errno(EMDB_NOSYMADDR));
1607	} else {
1608		u.s64 = gelf64_sym_search(gst->gst_asmap, gst->gst_aslen, addr);
1609		if (gelf64_to_sym(u.s64, sym) == NULL)
1610			return (set_errno(EMDB_NOSYMADDR));
1611	}
1612
1613	if ((flags & GST_EXACT) && (sym->st_value != addr))
1614		return (set_errno(EMDB_NOSYMADDR));
1615
1616	if (gst->gst_ssect != NULL) {
1617		name = (const char *)gst->gst_ssect->gs_data + sym->st_name;
1618		if (idp != NULL) {
1619			*idp = (u.sp - (caddr_t)gst->gst_dsect->gs_data) /
1620			    gst->gst_dsect->gs_shdr.sh_entsize;
1621		}
1622	} else {
1623		name = mdb_nv_get_name(u.dsp->ds_var);
1624		if (idp != NULL)
1625			*idp = u.dsp->ds_id;
1626	}
1627
1628	if (nbytes > 0) {
1629		(void) strncpy(buf, name, nbytes - 1);
1630		buf[nbytes - 1] = '\0';
1631	}
1632	return (0);
1633}
1634
1635int
1636mdb_gelf_symtab_lookup_by_name(mdb_gelf_symtab_t *gst, const char *name,
1637    GElf_Sym *sym, uint_t *idp)
1638{
1639	mdb_var_t *v;
1640
1641	if (gst != NULL && (v = mdb_nv_lookup(&gst->gst_nv, name)) != NULL) {
1642		if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32)
1643			(void) gelf32_to_sym(mdb_nv_get_cookie(v), sym);
1644		else
1645			(void) gelf64_to_sym(mdb_nv_get_cookie(v), sym);
1646
1647		if (idp != NULL) {
1648			if (gst->gst_file == NULL && gst->gst_dsect == NULL) {
1649				mdb_gelf_dsym_t *dsp = mdb_nv_get_cookie(v);
1650				*idp = dsp->ds_id;
1651			} else {
1652				*idp = ((uintptr_t)mdb_nv_get_cookie(v) -
1653				    (uintptr_t)gst->gst_dsect->gs_data) /
1654				    gst->gst_dsect->gs_shdr.sh_entsize;
1655			}
1656		}
1657
1658		return (0);
1659	}
1660
1661	return (set_errno(EMDB_NOSYM));
1662}
1663
1664int
1665mdb_gelf_symtab_lookup_by_file(mdb_gelf_symtab_t *gst, const char *file,
1666    const char *name, GElf_Sym *sym, uint_t *idp)
1667{
1668	GElf_Sym *(*s2gelf)(const void *, GElf_Sym *);
1669	size_t sym_size;
1670	caddr_t sp, ep;
1671	mdb_var_t *v;
1672
1673	if (gst == NULL)
1674		return (set_errno(EMDB_NOSYM));
1675
1676	if ((v = mdb_nv_lookup(&gst->gst_nv, file)) == NULL)
1677		return (set_errno(EMDB_NOOBJ));
1678
1679	if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
1680		s2gelf = (GElf_Sym *(*)(const void *, GElf_Sym *))gelf32_to_sym;
1681		sym_size = sizeof (Elf32_Sym);
1682	} else {
1683		s2gelf = (GElf_Sym *(*)(const void *, GElf_Sym *))gelf64_to_sym;
1684		sym_size = sizeof (Elf64_Sym);
1685	}
1686
1687	(void) s2gelf(mdb_nv_get_cookie(v), sym);
1688
1689	if (GELF_ST_TYPE(sym->st_info) != STT_FILE)
1690		return (set_errno(EMDB_NOOBJ));
1691
1692	ep = (caddr_t)gst->gst_dsect->gs_data + gst->gst_dsect->gs_shdr.sh_size;
1693	sp = (caddr_t)mdb_nv_get_cookie(v);
1694
1695	/*
1696	 * We assume that symbol lookups scoped by source file name are only
1697	 * relevant for userland debugging and are a relatively rare request,
1698	 * and so we use a simple but inefficient linear search with copying.
1699	 */
1700	for (sp += sym_size; sp < ep; sp += sym_size) {
1701		(void) s2gelf(sp, sym);	/* Convert native symbol to GElf */
1702
1703		if (GELF_ST_TYPE(sym->st_info) == STT_SECTION ||
1704		    GELF_ST_TYPE(sym->st_info) == STT_FILE ||
1705		    GELF_ST_BIND(sym->st_info) != STB_LOCAL)
1706			break;		/* End of this file's locals */
1707
1708		if (strcmp(mdb_gelf_sym_name(gst, sym), name) == 0) {
1709			if (idp != NULL) {
1710				*idp = (sp - (caddr_t)
1711				    gst->gst_dsect->gs_data) / sym_size;
1712			}
1713			return (0);
1714		}
1715	}
1716
1717	return (set_errno(EMDB_NOSYM));
1718}
1719
1720void
1721mdb_gelf_symtab_iter(mdb_gelf_symtab_t *gst, int (*func)(void *,
1722    const GElf_Sym *, const char *, uint_t), void *private)
1723{
1724	GElf_Sym *(*s2gelf)(const void *, GElf_Sym *);
1725	GElf_Sym sym, *symp;
1726	size_t sym_size;
1727
1728	if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
1729		s2gelf = (GElf_Sym *(*)(const void *, GElf_Sym *))gelf32_to_sym;
1730		sym_size = sizeof (Elf32_Sym);
1731	} else {
1732		s2gelf = (GElf_Sym *(*)(const void *, GElf_Sym *))gelf64_nocopy;
1733		sym_size = sizeof (Elf64_Sym);
1734	}
1735
1736	/*
1737	 * If this is a mutable symbol table, we iterate over the hash table
1738	 * of symbol names; otherwise we go iterate over the data buffer.  For
1739	 * non-mutable tables, this means that ::nm will show all symbols,
1740	 * including those with duplicate names (not present in gst_nv).
1741	 */
1742	if (gst->gst_file == NULL && gst->gst_dsect == NULL) {
1743		mdb_gelf_dsym_t *dsp;
1744		mdb_var_t *v;
1745
1746		mdb_nv_rewind(&gst->gst_nv);
1747		while ((v = mdb_nv_advance(&gst->gst_nv)) != NULL) {
1748			dsp = mdb_nv_get_cookie(v);
1749			symp = s2gelf(dsp, &sym);
1750			if (func(private, symp, mdb_nv_get_name(v),
1751			    dsp->ds_id) == -1)
1752				break;
1753		}
1754
1755	} else {
1756		const char *sbase = gst->gst_ssect->gs_data;
1757		caddr_t sp = gst->gst_dsect->gs_data;
1758		caddr_t ep = sp + gst->gst_dsect->gs_shdr.sh_size;
1759		uint_t i;
1760
1761		for (i = 0; sp < ep; sp += sym_size, i++) {
1762			symp = s2gelf(sp, &sym);
1763			if (func(private, symp, sbase + symp->st_name, i) == -1)
1764				break;
1765		}
1766	}
1767}
1768
1769static void
1770gelf_sym_to_32(const GElf_Sym *src, Elf32_Sym *dst)
1771{
1772	dst->st_name = src->st_name;
1773	dst->st_info = src->st_info;
1774	dst->st_other = src->st_other;
1775	dst->st_shndx = src->st_shndx;
1776	dst->st_value = (Elf32_Addr)src->st_value;
1777	dst->st_size = (Elf32_Word)src->st_size;
1778}
1779
1780static void
1781gelf_sym_to_64(const GElf_Sym *src, Elf64_Sym *dst)
1782{
1783	bcopy(src, dst, sizeof (Elf64_Sym));
1784}
1785
1786void
1787mdb_gelf_symtab_insert(mdb_gelf_symtab_t *gst,
1788    const char *name, const GElf_Sym *symp)
1789{
1790	mdb_gelf_dsym_t *dsp;
1791	mdb_var_t *v;
1792
1793	ASSERT(gst->gst_file == NULL && gst->gst_dsect == NULL);
1794	v = mdb_nv_lookup(&gst->gst_nv, name);
1795
1796	if (v == NULL) {
1797		char *s = mdb_alloc(strlen(name) + 1, UM_SLEEP);
1798		(void) strcpy(s, name);
1799
1800		dsp = mdb_alloc(sizeof (mdb_gelf_dsym_t), UM_SLEEP);
1801		dsp->ds_id = gst->gst_id++;
1802
1803		dsp->ds_var = mdb_nv_insert(&gst->gst_nv, s, NULL,
1804		    (uintptr_t)dsp, GST_NVFLG);
1805
1806		gst->gst_aslen++;
1807		ASSERT(gst->gst_aslen == mdb_nv_size(&gst->gst_nv));
1808
1809		if (gst->gst_aslen > gst->gst_asrsv) {
1810			mdb_free(gst->gst_asmap,
1811			    sizeof (void *) * gst->gst_asrsv);
1812
1813			gst->gst_asrsv = gst->gst_asrsv != 0 ?
1814			    gst->gst_asrsv * GST_GROW : GST_DEFSZ;
1815
1816			gst->gst_asmap = mdb_alloc(sizeof (void *) *
1817			    gst->gst_asrsv, UM_SLEEP);
1818		}
1819	} else
1820		dsp = mdb_nv_get_cookie(v);
1821
1822	mdb_dprintf(MDB_DBG_ELF, "added symbol (\"%s\", %llx)\n",
1823	    name, (u_longlong_t)symp->st_value);
1824
1825	bcopy(symp, &dsp->ds_sym, sizeof (GElf_Sym));
1826	dsp->ds_sym.st_name = (uintptr_t)mdb_nv_get_name(dsp->ds_var);
1827
1828	if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32) {
1829		gelf_sym_to_32(symp, &dsp->ds_u.ds_s32);
1830		gelf32_symtab_sort(gst);
1831	} else {
1832		gelf_sym_to_64(symp, &dsp->ds_u.ds_s64);
1833		gelf64_symtab_sort(gst);
1834	}
1835}
1836
1837void
1838mdb_gelf_symtab_delete(mdb_gelf_symtab_t *gst,
1839    const char *name, GElf_Sym *symp)
1840{
1841	mdb_var_t *v;
1842
1843	ASSERT(gst->gst_file == NULL && gst->gst_dsect == NULL);
1844	v = mdb_nv_lookup(&gst->gst_nv, name);
1845
1846	if (v != NULL) {
1847		char *name = (char *)mdb_nv_get_name(v);
1848		mdb_gelf_dsym_t *dsp = mdb_nv_get_cookie(v);
1849
1850		if (symp != NULL)
1851			bcopy(&dsp->ds_sym, symp, sizeof (GElf_Sym));
1852
1853		mdb_dprintf(MDB_DBG_ELF, "removed symbol (\"%s\", %llx)\n",
1854		    name, (u_longlong_t)dsp->ds_sym.st_value);
1855
1856		mdb_nv_remove(&gst->gst_nv, v);
1857		gst->gst_aslen--;
1858		ASSERT(gst->gst_aslen == mdb_nv_size(&gst->gst_nv));
1859
1860		mdb_free(name, strlen(name) + 1);
1861		mdb_free(dsp, sizeof (mdb_gelf_dsym_t));
1862
1863		if (gst->gst_ehdr->e_ident[EI_CLASS] == ELFCLASS32)
1864			gelf32_symtab_sort(gst);
1865		else
1866			gelf64_symtab_sort(gst);
1867	}
1868}
1869
1870static const GElf_Phdr *
1871gelf_phdr_lookup(mdb_gelf_file_t *gf, uintptr_t addr)
1872{
1873	const GElf_Phdr *gpp = gf->gf_phdrs;
1874	size_t i;
1875
1876	for (i = 0; i < gf->gf_npload; i++, gpp++) {
1877		if (addr >= gpp->p_vaddr && addr < gpp->p_vaddr + gpp->p_memsz)
1878			return (gpp);
1879	}
1880
1881	return (NULL);
1882}
1883
1884ssize_t
1885mdb_gelf_rw(mdb_gelf_file_t *gf, void *buf, size_t nbytes, uintptr_t addr,
1886    ssize_t (*prw)(mdb_io_t *, void *, size_t), mdb_gelf_rw_t rw)
1887{
1888	ssize_t resid = nbytes;
1889
1890	while (resid != 0) {
1891		const GElf_Phdr *php = gelf_phdr_lookup(gf, addr);
1892
1893		uintptr_t mapoff;
1894		ssize_t memlen, filelen, len = 0;
1895		off64_t off;
1896
1897		if (php == NULL)
1898			break; /* No mapping for this address */
1899
1900		mapoff = addr - php->p_vaddr;
1901		memlen = MIN(resid, php->p_memsz - mapoff);
1902		filelen = MIN(resid, php->p_filesz - mapoff);
1903		off = (off64_t)php->p_offset + mapoff;
1904
1905		if (filelen > 0 && (IOP_SEEK(gf->gf_io, off, SEEK_SET) != off ||
1906		    (len = prw(gf->gf_io, buf, filelen)) <= 0))
1907			break;
1908
1909		if (rw == GIO_READ && len == filelen && filelen < memlen) {
1910			bzero((char *)buf + len, memlen - filelen);
1911			len += memlen - filelen;
1912		}
1913
1914		resid -= len;
1915		addr += len;
1916		buf = (char *)buf + len;
1917	}
1918
1919	if (resid == nbytes && nbytes != 0)
1920		return (set_errno(EMDB_NOMAP));
1921
1922	return (nbytes - resid);
1923}
1924
1925mdb_gelf_sect_t *
1926mdb_gelf_sect_by_name(mdb_gelf_file_t *gf, const char *name)
1927{
1928	size_t i;
1929
1930	for (i = 0; i < gf->gf_shnum; i++) {
1931		if (strcmp(gf->gf_sects[i].gs_name, name) == 0)
1932			return (&gf->gf_sects[i]);
1933	}
1934
1935	return (NULL);
1936}
1937