1179185Sjb/*-
2210688Srpaulo * Copyright (c) 2010 The FreeBSD Foundation
3179185Sjb * Copyright (c) 2008 John Birrell (jb@freebsd.org)
4179185Sjb * All rights reserved.
5179185Sjb *
6210688Srpaulo * Portions of this software were developed by Rui Paulo under sponsorship
7210688Srpaulo * from the FreeBSD Foundation.
8210688Srpaulo *
9179185Sjb * Redistribution and use in source and binary forms, with or without
10179185Sjb * modification, are permitted provided that the following conditions
11179185Sjb * are met:
12179185Sjb * 1. Redistributions of source code must retain the above copyright
13179185Sjb *    notice, this list of conditions and the following disclaimer.
14179185Sjb * 2. Redistributions in binary form must reproduce the above copyright
15179185Sjb *    notice, this list of conditions and the following disclaimer in the
16179185Sjb *    documentation and/or other materials provided with the distribution.
17179185Sjb *
18179185Sjb * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19179185Sjb * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20179185Sjb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21179185Sjb * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22179185Sjb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23179185Sjb * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24179185Sjb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25179185Sjb * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26179185Sjb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27179185Sjb * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28179185Sjb * SUCH DAMAGE.
29179185Sjb *
30179185Sjb * $FreeBSD$
31179185Sjb */
32179185Sjb
33210688Srpaulo#include <sys/types.h>
34210688Srpaulo#include <sys/user.h>
35210688Srpaulo
36210688Srpaulo#include <assert.h>
37210688Srpaulo#include <err.h>
38179185Sjb#include <stdio.h>
39210688Srpaulo#include <libgen.h>
40210688Srpaulo#include <string.h>
41210688Srpaulo#include <stdlib.h>
42210688Srpaulo#include <fcntl.h>
43210688Srpaulo#include <string.h>
44210688Srpaulo#include <unistd.h>
45210688Srpaulo#include <libutil.h>
46179185Sjb
47210688Srpaulo#include "_libproc.h"
48210688Srpaulo
49268775Sdim#ifndef NO_CXA_DEMANGLE
50240156Srpauloextern char *__cxa_demangle(const char *, char *, size_t *, int *);
51268775Sdim#endif /* NO_CXA_DEMANGLE */
52240156Srpaulo
53210688Srpaulostatic void	proc_rdl2prmap(rd_loadobj_t *, prmap_t *);
54210688Srpaulo
55210688Srpaulostatic void
56240182Srpaulodemangle(const char *symbol, char *buf, size_t len)
57240182Srpaulo{
58268775Sdim#ifndef NO_CXA_DEMANGLE
59240182Srpaulo	char *dembuf;
60240182Srpaulo
61268775Sdim	if (symbol[0] == '_' && symbol[1] == 'Z' && symbol[2]) {
62269885Sdim		dembuf = __cxa_demangle(symbol, NULL, NULL, NULL);
63268775Sdim		if (!dembuf)
64268775Sdim			goto fail;
65268775Sdim		strlcpy(buf, dembuf, len);
66268775Sdim		free(dembuf);
67269885Sdim		return;
68268775Sdim	}
69240182Srpaulofail:
70268775Sdim#endif /* NO_CXA_DEMANGLE */
71240182Srpaulo	strlcpy(buf, symbol, len);
72240182Srpaulo}
73240182Srpaulo
74240182Srpaulostatic void
75210688Srpauloproc_rdl2prmap(rd_loadobj_t *rdl, prmap_t *map)
76210688Srpaulo{
77210688Srpaulo	map->pr_vaddr = rdl->rdl_saddr;
78210688Srpaulo	map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr;
79210688Srpaulo	map->pr_offset = rdl->rdl_offset;
80210688Srpaulo	map->pr_mflags = 0;
81210688Srpaulo	if (rdl->rdl_prot & RD_RDL_R)
82210688Srpaulo		map->pr_mflags |= MA_READ;
83210688Srpaulo	if (rdl->rdl_prot & RD_RDL_W)
84210688Srpaulo		map->pr_mflags |= MA_WRITE;
85210688Srpaulo	if (rdl->rdl_prot & RD_RDL_X)
86210688Srpaulo		map->pr_mflags |= MA_EXEC;
87210688Srpaulo	strlcpy(map->pr_mapname, rdl->rdl_path,
88210688Srpaulo	    sizeof(map->pr_mapname));
89210688Srpaulo}
90210688Srpaulo
91179185Sjbchar *
92179185Sjbproc_objname(struct proc_handle *p, uintptr_t addr, char *objname,
93179185Sjb    size_t objnamesz)
94179185Sjb{
95210688Srpaulo	size_t i;
96210688Srpaulo	rd_loadobj_t *rdl;
97210688Srpaulo
98210688Srpaulo	for (i = 0; i < p->nobjs; i++) {
99210688Srpaulo		rdl = &p->rdobjs[i];
100265073Smarkj		if (addr >= rdl->rdl_saddr && addr < rdl->rdl_eaddr) {
101210688Srpaulo			strlcpy(objname, rdl->rdl_path, objnamesz);
102210688Srpaulo			return (objname);
103210688Srpaulo		}
104210688Srpaulo	}
105179185Sjb	return (NULL);
106179185Sjb}
107179185Sjb
108210688Srpauloprmap_t *
109210688Srpauloproc_obj2map(struct proc_handle *p, const char *objname)
110210688Srpaulo{
111210688Srpaulo	size_t i;
112210688Srpaulo	prmap_t *map;
113210688Srpaulo	rd_loadobj_t *rdl;
114210688Srpaulo	char path[MAXPATHLEN];
115210688Srpaulo
116270731Smarkj	rdl = NULL;
117210688Srpaulo	for (i = 0; i < p->nobjs; i++) {
118270731Smarkj		basename_r(p->rdobjs[i].rdl_path, path);
119210688Srpaulo		if (strcmp(path, objname) == 0) {
120270731Smarkj			rdl = &p->rdobjs[i];
121270731Smarkj			break;
122210688Srpaulo		}
123210688Srpaulo	}
124270731Smarkj	if (rdl == NULL) {
125270731Smarkj		if (strcmp(objname, "a.out") == 0 && p->rdexec != NULL)
126270731Smarkj			rdl = p->rdexec;
127270731Smarkj		else
128270731Smarkj			return (NULL);
129270731Smarkj	}
130270731Smarkj
131270731Smarkj	if ((map = malloc(sizeof(*map))) == NULL)
132270731Smarkj		return (NULL);
133270731Smarkj	proc_rdl2prmap(rdl, map);
134270731Smarkj	return (map);
135210688Srpaulo}
136210688Srpaulo
137210688Srpauloint
138210688Srpauloproc_iter_objs(struct proc_handle *p, proc_map_f *func, void *cd)
139210688Srpaulo{
140210688Srpaulo	size_t i;
141210688Srpaulo	rd_loadobj_t *rdl;
142210688Srpaulo	prmap_t map;
143210688Srpaulo	char path[MAXPATHLEN];
144211184Srpaulo	char last[MAXPATHLEN];
145210688Srpaulo
146210688Srpaulo	if (p->nobjs == 0)
147210688Srpaulo		return (-1);
148211184Srpaulo	memset(last, 0, sizeof(last));
149210688Srpaulo	for (i = 0; i < p->nobjs; i++) {
150210688Srpaulo		rdl = &p->rdobjs[i];
151210688Srpaulo		proc_rdl2prmap(rdl, &map);
152210688Srpaulo		basename_r(rdl->rdl_path, path);
153211184Srpaulo		/*
154211184Srpaulo		 * We shouldn't call the callback twice with the same object.
155211184Srpaulo		 * To do that we are assuming the fact that if there are
156211184Srpaulo		 * repeated object names (i.e. different mappings for the
157211184Srpaulo		 * same object) they occur next to each other.
158211184Srpaulo		 */
159211184Srpaulo		if (strcmp(path, last) == 0)
160211184Srpaulo			continue;
161210688Srpaulo		(*func)(cd, &map, path);
162211184Srpaulo		strlcpy(last, path, sizeof(last));
163210688Srpaulo	}
164210688Srpaulo
165210688Srpaulo	return (0);
166210688Srpaulo}
167210688Srpaulo
168210688Srpauloprmap_t *
169179185Sjbproc_addr2map(struct proc_handle *p, uintptr_t addr)
170179185Sjb{
171210688Srpaulo	size_t i;
172210688Srpaulo	int cnt, lastvn = 0;
173210688Srpaulo	prmap_t *map;
174210688Srpaulo	rd_loadobj_t *rdl;
175210688Srpaulo	struct kinfo_vmentry *kves, *kve;
176210688Srpaulo
177210688Srpaulo	/*
178210688Srpaulo	 * If we don't have a cache of listed objects, we need to query
179210688Srpaulo	 * it ourselves.
180210688Srpaulo	 */
181210688Srpaulo	if (p->nobjs == 0) {
182210688Srpaulo		if ((kves = kinfo_getvmmap(p->pid, &cnt)) == NULL)
183210688Srpaulo			return (NULL);
184210688Srpaulo		for (i = 0; i < (size_t)cnt; i++) {
185210688Srpaulo			kve = kves + i;
186210688Srpaulo			if (kve->kve_type == KVME_TYPE_VNODE)
187210688Srpaulo				lastvn = i;
188265073Smarkj			if (addr >= kve->kve_start && addr < kve->kve_end) {
189210688Srpaulo				if ((map = malloc(sizeof(*map))) == NULL) {
190210688Srpaulo					free(kves);
191210688Srpaulo					return (NULL);
192210688Srpaulo				}
193210688Srpaulo				map->pr_vaddr = kve->kve_start;
194210688Srpaulo				map->pr_size = kve->kve_end - kve->kve_start;
195210688Srpaulo				map->pr_offset = kve->kve_offset;
196210688Srpaulo				map->pr_mflags = 0;
197210688Srpaulo				if (kve->kve_protection & KVME_PROT_READ)
198210688Srpaulo					map->pr_mflags |= MA_READ;
199210688Srpaulo				if (kve->kve_protection & KVME_PROT_WRITE)
200210688Srpaulo					map->pr_mflags |= MA_WRITE;
201210688Srpaulo				if (kve->kve_protection & KVME_PROT_EXEC)
202210688Srpaulo					map->pr_mflags |= MA_EXEC;
203210688Srpaulo				if (kve->kve_flags & KVME_FLAG_COW)
204210688Srpaulo					map->pr_mflags |= MA_COW;
205210688Srpaulo				if (kve->kve_flags & KVME_FLAG_NEEDS_COPY)
206210688Srpaulo					map->pr_mflags |= MA_NEEDS_COPY;
207210688Srpaulo				if (kve->kve_flags & KVME_FLAG_NOCOREDUMP)
208210688Srpaulo					map->pr_mflags |= MA_NOCOREDUMP;
209210688Srpaulo				strlcpy(map->pr_mapname, kves[lastvn].kve_path,
210210688Srpaulo				    sizeof(map->pr_mapname));
211210688Srpaulo				free(kves);
212210688Srpaulo				return (map);
213210688Srpaulo			}
214210688Srpaulo		}
215210688Srpaulo		free(kves);
216210688Srpaulo		return (NULL);
217210688Srpaulo	}
218210688Srpaulo
219210688Srpaulo	for (i = 0; i < p->nobjs; i++) {
220210688Srpaulo		rdl = &p->rdobjs[i];
221265073Smarkj		if (addr >= rdl->rdl_saddr && addr < rdl->rdl_eaddr) {
222210688Srpaulo			if ((map = malloc(sizeof(*map))) == NULL)
223210688Srpaulo				return (NULL);
224210688Srpaulo			proc_rdl2prmap(rdl, map);
225210688Srpaulo			return (map);
226210688Srpaulo		}
227210688Srpaulo	}
228179185Sjb	return (NULL);
229179185Sjb}
230179185Sjb
231179185Sjbint
232179185Sjbproc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name,
233210688Srpaulo    size_t namesz, GElf_Sym *symcopy)
234179185Sjb{
235210688Srpaulo	Elf *e;
236210688Srpaulo	Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL;
237210688Srpaulo	Elf_Data *data;
238210688Srpaulo	GElf_Shdr shdr;
239210688Srpaulo	GElf_Sym sym;
240210688Srpaulo	GElf_Ehdr ehdr;
241210688Srpaulo	int fd, error = -1;
242210688Srpaulo	size_t i;
243210688Srpaulo	uint64_t rsym;
244210688Srpaulo	prmap_t *map;
245210688Srpaulo	char *s;
246210688Srpaulo	unsigned long symtabstridx = 0, dynsymstridx = 0;
247210688Srpaulo
248210688Srpaulo	if ((map = proc_addr2map(p, addr)) == NULL)
249210688Srpaulo		return (-1);
250259895Smarkj	if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) {
251259895Smarkj		DPRINTF("ERROR: open %s failed", map->pr_mapname);
252210688Srpaulo		goto err0;
253210688Srpaulo	}
254210688Srpaulo	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
255259895Smarkj		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
256210688Srpaulo		goto err1;
257210688Srpaulo	}
258210688Srpaulo	if (gelf_getehdr(e, &ehdr) == NULL) {
259259895Smarkj		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
260210688Srpaulo		goto err2;
261210688Srpaulo	}
262210688Srpaulo	/*
263210688Srpaulo	 * Find the index of the STRTAB and SYMTAB sections to locate
264210688Srpaulo	 * symbol names.
265210688Srpaulo	 */
266210688Srpaulo	scn = NULL;
267210688Srpaulo	while ((scn = elf_nextscn(e, scn)) != NULL) {
268210688Srpaulo		gelf_getshdr(scn, &shdr);
269210688Srpaulo		switch (shdr.sh_type) {
270210688Srpaulo		case SHT_SYMTAB:
271210688Srpaulo			symtabscn = scn;
272210688Srpaulo			symtabstridx = shdr.sh_link;
273210688Srpaulo			break;
274210688Srpaulo		case SHT_DYNSYM:
275210688Srpaulo			dynsymscn = scn;
276210688Srpaulo			dynsymstridx = shdr.sh_link;
277210688Srpaulo			break;
278210688Srpaulo		default:
279210688Srpaulo			break;
280210688Srpaulo		}
281210688Srpaulo	}
282210688Srpaulo	/*
283210688Srpaulo	 * Iterate over the Dynamic Symbols table to find the symbol.
284210688Srpaulo	 * Then look up the string name in STRTAB (.dynstr)
285210688Srpaulo	 */
286210688Srpaulo	if ((data = elf_getdata(dynsymscn, NULL)) == NULL) {
287259895Smarkj		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
288240040Srpaulo		goto symtab;
289210688Srpaulo	}
290210688Srpaulo	i = 0;
291210688Srpaulo	while (gelf_getsym(data, i++, &sym) != NULL) {
292210688Srpaulo		/*
293210688Srpaulo		 * Calculate the address mapped to the virtual memory
294210688Srpaulo		 * by rtld.
295210688Srpaulo		 */
296259972Smarkj		if (ehdr.e_type != ET_EXEC)
297259972Smarkj			rsym = map->pr_vaddr + sym.st_value;
298259972Smarkj		else
299259972Smarkj			rsym = sym.st_value;
300259965Smarkj		if (addr >= rsym && addr < rsym + sym.st_size) {
301210688Srpaulo			s = elf_strptr(e, dynsymstridx, sym.st_name);
302210688Srpaulo			if (s) {
303268775Sdim				demangle(s, name, namesz);
304210688Srpaulo				memcpy(symcopy, &sym, sizeof(sym));
305210688Srpaulo				/*
306210688Srpaulo				 * DTrace expects the st_value to contain
307210688Srpaulo				 * only the address relative to the start of
308210688Srpaulo				 * the function.
309210688Srpaulo				 */
310210688Srpaulo				symcopy->st_value = rsym;
311254177Srpaulo				error = 0;
312210688Srpaulo				goto out;
313210688Srpaulo			}
314210688Srpaulo		}
315210688Srpaulo	}
316240040Srpaulosymtab:
317210688Srpaulo	/*
318210688Srpaulo	 * Iterate over the Symbols Table to find the symbol.
319210688Srpaulo	 * Then look up the string name in STRTAB (.dynstr)
320210688Srpaulo	 */
321210688Srpaulo	if ((data = elf_getdata(symtabscn, NULL)) == NULL) {
322259895Smarkj		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
323210688Srpaulo		goto err2;
324210688Srpaulo	}
325210688Srpaulo	i = 0;
326210688Srpaulo	while (gelf_getsym(data, i++, &sym) != NULL) {
327210688Srpaulo		/*
328210688Srpaulo		 * Calculate the address mapped to the virtual memory
329210688Srpaulo		 * by rtld.
330210688Srpaulo		 */
331210688Srpaulo		if (ehdr.e_type != ET_EXEC)
332210688Srpaulo			rsym = map->pr_vaddr + sym.st_value;
333210688Srpaulo		else
334210688Srpaulo			rsym = sym.st_value;
335259965Smarkj		if (addr >= rsym && addr < rsym + sym.st_size) {
336210688Srpaulo			s = elf_strptr(e, symtabstridx, sym.st_name);
337210688Srpaulo			if (s) {
338268775Sdim				demangle(s, name, namesz);
339210688Srpaulo				memcpy(symcopy, &sym, sizeof(sym));
340210688Srpaulo				/*
341210688Srpaulo				 * DTrace expects the st_value to contain
342210688Srpaulo				 * only the address relative to the start of
343210688Srpaulo				 * the function.
344210688Srpaulo				 */
345210688Srpaulo				symcopy->st_value = rsym;
346210688Srpaulo				error = 0;
347210688Srpaulo				goto out;
348210688Srpaulo			}
349210688Srpaulo		}
350210688Srpaulo	}
351210688Srpauloout:
352210688Srpauloerr2:
353210688Srpaulo	elf_end(e);
354210688Srpauloerr1:
355210688Srpaulo	close(fd);
356210688Srpauloerr0:
357210688Srpaulo	free(map);
358210688Srpaulo	return (error);
359179185Sjb}
360179185Sjb
361210688Srpauloprmap_t *
362179185Sjbproc_name2map(struct proc_handle *p, const char *name)
363179185Sjb{
364210688Srpaulo	size_t i;
365210688Srpaulo	int cnt;
366210688Srpaulo	prmap_t *map;
367210688Srpaulo	char tmppath[MAXPATHLEN];
368210688Srpaulo	struct kinfo_vmentry *kves, *kve;
369210688Srpaulo	rd_loadobj_t *rdl;
370210688Srpaulo
371210688Srpaulo	/*
372210688Srpaulo	 * If we haven't iterated over the list of loaded objects,
373210688Srpaulo	 * librtld_db isn't yet initialized and it's very likely
374210688Srpaulo	 * that librtld_db called us. We need to do the heavy
375210688Srpaulo	 * lifting here to find the symbol librtld_db is looking for.
376210688Srpaulo	 */
377210688Srpaulo	if (p->nobjs == 0) {
378210688Srpaulo		if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL)
379210688Srpaulo			return (NULL);
380210688Srpaulo		for (i = 0; i < (size_t)cnt; i++) {
381210688Srpaulo			kve = kves + i;
382210688Srpaulo			basename_r(kve->kve_path, tmppath);
383210688Srpaulo			if (strcmp(tmppath, name) == 0) {
384210688Srpaulo				map = proc_addr2map(p, kve->kve_start);
385210688Srpaulo				free(kves);
386210688Srpaulo				return (map);
387210688Srpaulo			}
388210688Srpaulo		}
389210688Srpaulo		free(kves);
390210688Srpaulo		return (NULL);
391210688Srpaulo	}
392270731Smarkj	if ((name == NULL || strcmp(name, "a.out") == 0) &&
393270731Smarkj	    p->rdexec != NULL) {
394270731Smarkj		map = proc_addr2map(p, p->rdexec->rdl_saddr);
395210688Srpaulo		return (map);
396210688Srpaulo	}
397210688Srpaulo	for (i = 0; i < p->nobjs; i++) {
398210688Srpaulo		rdl = &p->rdobjs[i];
399210688Srpaulo		basename_r(rdl->rdl_path, tmppath);
400210688Srpaulo		if (strcmp(tmppath, name) == 0) {
401210688Srpaulo			if ((map = malloc(sizeof(*map))) == NULL)
402210688Srpaulo				return (NULL);
403210688Srpaulo			proc_rdl2prmap(rdl, map);
404210688Srpaulo			return (map);
405210688Srpaulo		}
406210688Srpaulo	}
407210688Srpaulo
408179185Sjb	return (NULL);
409179185Sjb}
410179185Sjb
411179185Sjbint
412179185Sjbproc_name2sym(struct proc_handle *p, const char *object, const char *symbol,
413210688Srpaulo    GElf_Sym *symcopy)
414179185Sjb{
415210688Srpaulo	Elf *e;
416210688Srpaulo	Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL;
417210688Srpaulo	Elf_Data *data;
418210688Srpaulo	GElf_Shdr shdr;
419210688Srpaulo	GElf_Sym sym;
420210688Srpaulo	GElf_Ehdr ehdr;
421210688Srpaulo	int fd, error = -1;
422210688Srpaulo	size_t i;
423210688Srpaulo	prmap_t *map;
424210688Srpaulo	char *s;
425210688Srpaulo	unsigned long symtabstridx = 0, dynsymstridx = 0;
426210688Srpaulo
427210688Srpaulo	if ((map = proc_name2map(p, object)) == NULL) {
428259895Smarkj		DPRINTFX("ERROR: couldn't find object %s", object);
429210688Srpaulo		goto err0;
430210688Srpaulo	}
431210688Srpaulo	if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) {
432210688Srpaulo		DPRINTF("ERROR: open %s failed", map->pr_mapname);
433210688Srpaulo		goto err0;
434210688Srpaulo	}
435210688Srpaulo	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
436259895Smarkj		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
437210688Srpaulo		goto err1;
438210688Srpaulo	}
439210688Srpaulo	if (gelf_getehdr(e, &ehdr) == NULL) {
440259895Smarkj		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
441210688Srpaulo		goto err2;
442210688Srpaulo	}
443210688Srpaulo	/*
444210688Srpaulo	 * Find the index of the STRTAB and SYMTAB sections to locate
445210688Srpaulo	 * symbol names.
446210688Srpaulo	 */
447210688Srpaulo	scn = NULL;
448210688Srpaulo	while ((scn = elf_nextscn(e, scn)) != NULL) {
449210688Srpaulo		gelf_getshdr(scn, &shdr);
450210688Srpaulo		switch (shdr.sh_type) {
451210688Srpaulo		case SHT_SYMTAB:
452210688Srpaulo			symtabscn = scn;
453210688Srpaulo			symtabstridx = shdr.sh_link;
454210688Srpaulo			break;
455210688Srpaulo		case SHT_DYNSYM:
456210688Srpaulo			dynsymscn = scn;
457210688Srpaulo			dynsymstridx = shdr.sh_link;
458210688Srpaulo			break;
459210688Srpaulo		default:
460210688Srpaulo			break;
461210688Srpaulo		}
462210688Srpaulo	}
463210688Srpaulo	/*
464210688Srpaulo	 * Iterate over the Dynamic Symbols table to find the symbol.
465210688Srpaulo	 * Then look up the string name in STRTAB (.dynstr)
466210688Srpaulo	 */
467240040Srpaulo	if ((data = elf_getdata(dynsymscn, NULL))) {
468240040Srpaulo		i = 0;
469240040Srpaulo		while (gelf_getsym(data, i++, &sym) != NULL) {
470240040Srpaulo			s = elf_strptr(e, dynsymstridx, sym.st_name);
471240040Srpaulo			if (s && strcmp(s, symbol) == 0) {
472240040Srpaulo				memcpy(symcopy, &sym, sizeof(sym));
473259972Smarkj				if (ehdr.e_type != ET_EXEC)
474259972Smarkj					symcopy->st_value += map->pr_vaddr;
475240040Srpaulo				error = 0;
476240040Srpaulo				goto out;
477240040Srpaulo			}
478210688Srpaulo		}
479210688Srpaulo	}
480210688Srpaulo	/*
481210688Srpaulo	 * Iterate over the Symbols Table to find the symbol.
482210688Srpaulo	 * Then look up the string name in STRTAB (.dynstr)
483210688Srpaulo	 */
484240040Srpaulo	if ((data = elf_getdata(symtabscn, NULL))) {
485240040Srpaulo		i = 0;
486240040Srpaulo		while (gelf_getsym(data, i++, &sym) != NULL) {
487240040Srpaulo			s = elf_strptr(e, symtabstridx, sym.st_name);
488240040Srpaulo			if (s && strcmp(s, symbol) == 0) {
489240040Srpaulo				memcpy(symcopy, &sym, sizeof(sym));
490259972Smarkj				if (ehdr.e_type != ET_EXEC)
491259972Smarkj					symcopy->st_value += map->pr_vaddr;
492240040Srpaulo				error = 0;
493240040Srpaulo				goto out;
494240040Srpaulo			}
495210688Srpaulo		}
496210688Srpaulo	}
497210688Srpauloout:
498259972Smarkj	DPRINTFX("found addr 0x%lx for %s", symcopy->st_value, symbol);
499210688Srpauloerr2:
500210688Srpaulo	elf_end(e);
501210688Srpauloerr1:
502210688Srpaulo	close(fd);
503210688Srpauloerr0:
504210688Srpaulo	free(map);
505210688Srpaulo
506210688Srpaulo	return (error);
507179185Sjb}
508210688Srpaulo
509210688Srpaulo
510210688Srpauloint
511210688Srpauloproc_iter_symbyaddr(struct proc_handle *p, const char *object, int which,
512210688Srpaulo    int mask, proc_sym_f *func, void *cd)
513210688Srpaulo{
514210688Srpaulo	Elf *e;
515210688Srpaulo	int i, fd;
516210688Srpaulo	prmap_t *map;
517210688Srpaulo	Elf_Scn *scn, *foundscn = NULL;
518210688Srpaulo	Elf_Data *data;
519259972Smarkj	GElf_Ehdr ehdr;
520210688Srpaulo	GElf_Shdr shdr;
521210688Srpaulo	GElf_Sym sym;
522210688Srpaulo	unsigned long stridx = -1;
523210688Srpaulo	char *s;
524210688Srpaulo	int error = -1;
525210688Srpaulo
526210688Srpaulo	if ((map = proc_name2map(p, object)) == NULL)
527210688Srpaulo		return (-1);
528210688Srpaulo	if ((fd = open(map->pr_mapname, O_RDONLY)) < 0) {
529259895Smarkj		DPRINTF("ERROR: open %s failed", map->pr_mapname);
530210688Srpaulo		goto err0;
531210688Srpaulo	}
532210688Srpaulo	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
533259895Smarkj		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
534210688Srpaulo		goto err1;
535210688Srpaulo	}
536259972Smarkj	if (gelf_getehdr(e, &ehdr) == NULL) {
537259972Smarkj		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
538259972Smarkj		goto err2;
539259972Smarkj	}
540210688Srpaulo	/*
541210688Srpaulo	 * Find the section we are looking for.
542210688Srpaulo	 */
543210688Srpaulo	scn = NULL;
544210688Srpaulo	while ((scn = elf_nextscn(e, scn)) != NULL) {
545210688Srpaulo		gelf_getshdr(scn, &shdr);
546210688Srpaulo		if (which == PR_SYMTAB &&
547210688Srpaulo		    shdr.sh_type == SHT_SYMTAB) {
548210688Srpaulo			foundscn = scn;
549210688Srpaulo			break;
550210688Srpaulo		} else if (which == PR_DYNSYM &&
551210688Srpaulo		    shdr.sh_type == SHT_DYNSYM) {
552210688Srpaulo			foundscn = scn;
553210688Srpaulo			break;
554210688Srpaulo		}
555210688Srpaulo	}
556210688Srpaulo	if (!foundscn)
557210688Srpaulo		return (-1);
558210688Srpaulo	stridx = shdr.sh_link;
559210688Srpaulo	if ((data = elf_getdata(foundscn, NULL)) == NULL) {
560259895Smarkj		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
561210688Srpaulo		goto err2;
562210688Srpaulo	}
563210688Srpaulo	i = 0;
564210688Srpaulo	while (gelf_getsym(data, i++, &sym) != NULL) {
565210688Srpaulo		if (GELF_ST_BIND(sym.st_info) == STB_LOCAL &&
566210688Srpaulo		    (mask & BIND_LOCAL) == 0)
567210688Srpaulo			continue;
568210688Srpaulo		if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL &&
569210688Srpaulo		    (mask & BIND_GLOBAL) == 0)
570210688Srpaulo			continue;
571210688Srpaulo		if (GELF_ST_BIND(sym.st_info) == STB_WEAK &&
572210688Srpaulo		    (mask & BIND_WEAK) == 0)
573210688Srpaulo			continue;
574210688Srpaulo		if (GELF_ST_TYPE(sym.st_info) == STT_NOTYPE &&
575210688Srpaulo		    (mask & TYPE_NOTYPE) == 0)
576210688Srpaulo			continue;
577210688Srpaulo		if (GELF_ST_TYPE(sym.st_info) == STT_OBJECT &&
578210688Srpaulo		    (mask & TYPE_OBJECT) == 0)
579210688Srpaulo			continue;
580210688Srpaulo		if (GELF_ST_TYPE(sym.st_info) == STT_FUNC &&
581210688Srpaulo		    (mask & TYPE_FUNC) == 0)
582210688Srpaulo			continue;
583210688Srpaulo		if (GELF_ST_TYPE(sym.st_info) == STT_SECTION &&
584210688Srpaulo		    (mask & TYPE_SECTION) == 0)
585210688Srpaulo			continue;
586210688Srpaulo		if (GELF_ST_TYPE(sym.st_info) == STT_FILE &&
587210688Srpaulo		    (mask & TYPE_FILE) == 0)
588210688Srpaulo			continue;
589210688Srpaulo		s = elf_strptr(e, stridx, sym.st_name);
590259972Smarkj		if (ehdr.e_type != ET_EXEC)
591259972Smarkj			sym.st_value += map->pr_vaddr;
592210688Srpaulo		(*func)(cd, &sym, s);
593210688Srpaulo	}
594210688Srpaulo	error = 0;
595210688Srpauloerr2:
596210688Srpaulo	elf_end(e);
597210688Srpauloerr1:
598210688Srpaulo	close(fd);
599210688Srpauloerr0:
600210688Srpaulo	free(map);
601210688Srpaulo	return (error);
602210688Srpaulo}
603