proc_sym.c revision 259970
184865Sobrien/*-
2218822Sdim * Copyright (c) 2010 The FreeBSD Foundation
384865Sobrien * Copyright (c) 2008 John Birrell (jb@freebsd.org)
4218822Sdim * All rights reserved.
584865Sobrien *
684865Sobrien * Portions of this software were developed by Rui Paulo under sponsorship
784865Sobrien * from the FreeBSD Foundation.
884865Sobrien *
984865Sobrien * Redistribution and use in source and binary forms, with or without
1084865Sobrien * modification, are permitted provided that the following conditions
1184865Sobrien * are met:
1284865Sobrien * 1. Redistributions of source code must retain the above copyright
1384865Sobrien *    notice, this list of conditions and the following disclaimer.
1484865Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1584865Sobrien *    notice, this list of conditions and the following disclaimer in the
1684865Sobrien *    documentation and/or other materials provided with the distribution.
1784865Sobrien *
1884865Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1984865Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2084865Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2184865Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22218822Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2384865Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24218822Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2584865Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2689857Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2784865Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2884865Sobrien * SUCH DAMAGE.
2989857Sobrien *
3084865Sobrien * $FreeBSD: stable/9/lib/libproc/proc_sym.c 259970 2013-12-27 22:54:38Z markj $
3184865Sobrien */
3289857Sobrien
3384865Sobrien#include <sys/types.h>
34130561Sobrien#include <sys/user.h>
35130561Sobrien
36130561Sobrien#include <assert.h>
37130561Sobrien#include <err.h>
38130561Sobrien#include <stdio.h>
39130561Sobrien#include <libgen.h>
4089857Sobrien#include <string.h>
4189857Sobrien#include <stdlib.h>
42130561Sobrien#include <fcntl.h>
43130561Sobrien#include <string.h>
44130561Sobrien#include <unistd.h>
45130561Sobrien#include <libutil.h>
46130561Sobrien
47130561Sobrien#include "_libproc.h"
48130561Sobrien
49130561Sobrienstatic void	proc_rdl2prmap(rd_loadobj_t *, prmap_t *);
50130561Sobrien
51130561Sobrienstatic void
52130561Sobrienproc_rdl2prmap(rd_loadobj_t *rdl, prmap_t *map)
5389857Sobrien{
54130561Sobrien	map->pr_vaddr = rdl->rdl_saddr;
55130561Sobrien	map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr;
56130561Sobrien	map->pr_offset = rdl->rdl_offset;
57218822Sdim	map->pr_mflags = 0;
58130561Sobrien	if (rdl->rdl_prot & RD_RDL_R)
59130561Sobrien		map->pr_mflags |= MA_READ;
60130561Sobrien	if (rdl->rdl_prot & RD_RDL_W)
61130561Sobrien		map->pr_mflags |= MA_WRITE;
62130561Sobrien	if (rdl->rdl_prot & RD_RDL_X)
63130561Sobrien		map->pr_mflags |= MA_EXEC;
64130561Sobrien	strlcpy(map->pr_mapname, rdl->rdl_path,
65130561Sobrien	    sizeof(map->pr_mapname));
66130561Sobrien}
67130561Sobrien
68130561Sobrienchar *
69130561Sobrienproc_objname(struct proc_handle *p, uintptr_t addr, char *objname,
7089857Sobrien    size_t objnamesz)
71104834Sobrien{
72130561Sobrien	size_t i;
73130561Sobrien	rd_loadobj_t *rdl;
7489857Sobrien
7589857Sobrien	for (i = 0; i < p->nobjs; i++) {
7689857Sobrien		rdl = &p->rdobjs[i];
7789857Sobrien		if (addr >= rdl->rdl_saddr && addr <= rdl->rdl_eaddr) {
7889857Sobrien			strlcpy(objname, rdl->rdl_path, objnamesz);
7989857Sobrien			return (objname);
8089857Sobrien		}
8189857Sobrien	}
8289857Sobrien	return (NULL);
8389857Sobrien}
8489857Sobrien
8589857Sobrienprmap_t *
8689857Sobrienproc_obj2map(struct proc_handle *p, const char *objname)
8789857Sobrien{
8889857Sobrien	size_t i;
8989857Sobrien	prmap_t *map;
9089857Sobrien	rd_loadobj_t *rdl;
91104834Sobrien	char path[MAXPATHLEN];
9289857Sobrien
9389857Sobrien	for (i = 0; i < p->nobjs; i++) {
9489857Sobrien		rdl = &p->rdobjs[i];
9589857Sobrien		basename_r(rdl->rdl_path, path);
96218822Sdim		if (strcmp(path, objname) == 0) {
9789857Sobrien			if ((map = malloc(sizeof(*map))) == NULL)
98130561Sobrien				return (NULL);
99130561Sobrien			proc_rdl2prmap(rdl, map);
100130561Sobrien			return (map);
101104834Sobrien		}
102130561Sobrien	}
103130561Sobrien	return (NULL);
104130561Sobrien}
105130561Sobrien
10689857Sobrienint
10789857Sobrienproc_iter_objs(struct proc_handle *p, proc_map_f *func, void *cd)
10889857Sobrien{
10989857Sobrien	size_t i;
11089857Sobrien	rd_loadobj_t *rdl;
11189857Sobrien	prmap_t map;
11289857Sobrien	char path[MAXPATHLEN];
11389857Sobrien	char last[MAXPATHLEN];
11489857Sobrien
11589857Sobrien	if (p->nobjs == 0)
11689857Sobrien		return (-1);
11789857Sobrien	memset(last, 0, sizeof(last));
11889857Sobrien	for (i = 0; i < p->nobjs; i++) {
11989857Sobrien		rdl = &p->rdobjs[i];
12089857Sobrien		proc_rdl2prmap(rdl, &map);
12189857Sobrien		basename_r(rdl->rdl_path, path);
12289857Sobrien		/*
12389857Sobrien		 * We shouldn't call the callback twice with the same object.
12489857Sobrien		 * To do that we are assuming the fact that if there are
12589857Sobrien		 * repeated object names (i.e. different mappings for the
126104834Sobrien		 * same object) they occur next to each other.
127104834Sobrien		 */
128104834Sobrien		if (strcmp(path, last) == 0)
12989857Sobrien			continue;
13089857Sobrien		(*func)(cd, &map, path);
13189857Sobrien		strlcpy(last, path, sizeof(last));
13284865Sobrien	}
13384865Sobrien
134130561Sobrien	return (0);
135130561Sobrien}
136130561Sobrien
13789857Sobrienprmap_t *
138130561Sobrienproc_addr2map(struct proc_handle *p, uintptr_t addr)
13989857Sobrien{
140130561Sobrien	size_t i;
141130561Sobrien	int cnt, lastvn = 0;
142130561Sobrien	prmap_t *map;
143130561Sobrien	rd_loadobj_t *rdl;
14489857Sobrien	struct kinfo_vmentry *kves, *kve;
14589857Sobrien
14689857Sobrien	/*
14789857Sobrien	 * If we don't have a cache of listed objects, we need to query
14889857Sobrien	 * it ourselves.
14989857Sobrien	 */
15089857Sobrien	if (p->nobjs == 0) {
15189857Sobrien		if ((kves = kinfo_getvmmap(p->pid, &cnt)) == NULL)
15289857Sobrien			return (NULL);
15389857Sobrien		for (i = 0; i < (size_t)cnt; i++) {
15489857Sobrien			kve = kves + i;
15589857Sobrien			if (kve->kve_type == KVME_TYPE_VNODE)
156130561Sobrien				lastvn = i;
15789857Sobrien			if (addr >= kve->kve_start && addr <= kve->kve_end) {
15889857Sobrien				if ((map = malloc(sizeof(*map))) == NULL) {
159130561Sobrien					free(kves);
16089857Sobrien					return (NULL);
16189857Sobrien				}
16289857Sobrien				map->pr_vaddr = kve->kve_start;
16389857Sobrien				map->pr_size = kve->kve_end - kve->kve_start;
164130561Sobrien				map->pr_offset = kve->kve_offset;
165130561Sobrien				map->pr_mflags = 0;
166130561Sobrien				if (kve->kve_protection & KVME_PROT_READ)
167130561Sobrien					map->pr_mflags |= MA_READ;
16889857Sobrien				if (kve->kve_protection & KVME_PROT_WRITE)
16989857Sobrien					map->pr_mflags |= MA_WRITE;
17089857Sobrien				if (kve->kve_protection & KVME_PROT_EXEC)
17189857Sobrien					map->pr_mflags |= MA_EXEC;
172130561Sobrien				if (kve->kve_flags & KVME_FLAG_COW)
173130561Sobrien					map->pr_mflags |= MA_COW;
174130561Sobrien				if (kve->kve_flags & KVME_FLAG_NEEDS_COPY)
175130561Sobrien					map->pr_mflags |= MA_NEEDS_COPY;
176130561Sobrien				if (kve->kve_flags & KVME_FLAG_NOCOREDUMP)
177130561Sobrien					map->pr_mflags |= MA_NOCOREDUMP;
178130561Sobrien				strlcpy(map->pr_mapname, kves[lastvn].kve_path,
179218822Sdim				    sizeof(map->pr_mapname));
18084865Sobrien				free(kves);
181104834Sobrien				return (map);
182130561Sobrien			}
183130561Sobrien		}
18489857Sobrien		free(kves);
185130561Sobrien		return (NULL);
186104834Sobrien	}
187130561Sobrien
188104834Sobrien	for (i = 0; i < p->nobjs; i++) {
189130561Sobrien		rdl = &p->rdobjs[i];
190104834Sobrien		if (addr >= rdl->rdl_saddr && addr <= rdl->rdl_eaddr) {
191130561Sobrien			if ((map = malloc(sizeof(*map))) == NULL)
192104834Sobrien				return (NULL);
193104834Sobrien			proc_rdl2prmap(rdl, map);
194130561Sobrien			return (map);
195104834Sobrien		}
196104834Sobrien	}
197130561Sobrien	return (NULL);
198130561Sobrien}
199130561Sobrien
200130561Sobrienint
201104834Sobrienproc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name,
202130561Sobrien    size_t namesz, GElf_Sym *symcopy)
203130561Sobrien{
204130561Sobrien	Elf *e;
205130561Sobrien	Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL;
206130561Sobrien	Elf_Data *data;
207130561Sobrien	GElf_Shdr shdr;
208130561Sobrien	GElf_Sym sym;
209130561Sobrien	GElf_Ehdr ehdr;
210130561Sobrien	int fd, error = -1;
211130561Sobrien	size_t i;
212104834Sobrien	uint64_t rsym;
213130561Sobrien	prmap_t *map;
214130561Sobrien	char *s;
215130561Sobrien	unsigned long symtabstridx = 0, dynsymstridx = 0;
216130561Sobrien
217104834Sobrien	if ((map = proc_addr2map(p, addr)) == NULL)
218104834Sobrien		return (-1);
219130561Sobrien	if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) {
220104834Sobrien		DPRINTF("ERROR: open %s failed", map->pr_mapname);
221130561Sobrien		goto err0;
222130561Sobrien	}
223130561Sobrien	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
224130561Sobrien		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
225104834Sobrien		goto err1;
226104834Sobrien	}
227130561Sobrien	if (gelf_getehdr(e, &ehdr) == NULL) {
228104834Sobrien		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
229104834Sobrien		goto err2;
230104834Sobrien	}
231104834Sobrien	/*
232104834Sobrien	 * Find the index of the STRTAB and SYMTAB sections to locate
233104834Sobrien	 * symbol names.
234104834Sobrien	 */
235104834Sobrien	scn = NULL;
23684865Sobrien	while ((scn = elf_nextscn(e, scn)) != NULL) {
23784865Sobrien		gelf_getshdr(scn, &shdr);
23884865Sobrien		switch (shdr.sh_type) {
239130561Sobrien		case SHT_SYMTAB:
24084865Sobrien			symtabscn = scn;
24184865Sobrien			symtabstridx = shdr.sh_link;
24284865Sobrien			break;
24384865Sobrien		case SHT_DYNSYM:
24489857Sobrien			dynsymscn = scn;
24584865Sobrien			dynsymstridx = shdr.sh_link;
24689857Sobrien			break;
24784865Sobrien		default:
248130561Sobrien			break;
24984865Sobrien		}
25084865Sobrien	}
25184865Sobrien	/*
25284865Sobrien	 * Iterate over the Dynamic Symbols table to find the symbol.
25384865Sobrien	 * Then look up the string name in STRTAB (.dynstr)
25484865Sobrien	 */
25584865Sobrien	if ((data = elf_getdata(dynsymscn, NULL)) == NULL) {
25684865Sobrien		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
25784865Sobrien		goto symtab;
25884865Sobrien	}
25984865Sobrien	i = 0;
26084865Sobrien	while (gelf_getsym(data, i++, &sym) != NULL) {
26184865Sobrien		/*
26284865Sobrien		 * Calculate the address mapped to the virtual memory
26384865Sobrien		 * by rtld.
26489857Sobrien		 */
265104834Sobrien		rsym = map->pr_vaddr + sym.st_value;
26689857Sobrien		if (addr >= rsym && addr < rsym + sym.st_size) {
267130561Sobrien			s = elf_strptr(e, dynsymstridx, sym.st_name);
26884865Sobrien			if (s) {
26984865Sobrien				strlcpy(name, s, namesz);
27084865Sobrien				memcpy(symcopy, &sym, sizeof(sym));
27184865Sobrien				/*
272130561Sobrien				 * DTrace expects the st_value to contain
27384865Sobrien				 * only the address relative to the start of
27484865Sobrien				 * the function.
27584865Sobrien				 */
27684865Sobrien				symcopy->st_value = rsym;
27784865Sobrien				error = 0;
27884865Sobrien				goto out;
27984865Sobrien			}
28084865Sobrien		}
281130561Sobrien	}
28284865Sobriensymtab:
28384865Sobrien	/*
28484865Sobrien	 * Iterate over the Symbols Table to find the symbol.
28584865Sobrien	 * Then look up the string name in STRTAB (.dynstr)
28684865Sobrien	 */
28784865Sobrien	if (symtabscn == NULL)
28884865Sobrien		goto err2;
28984865Sobrien	if ((data = elf_getdata(symtabscn, NULL)) == NULL) {
29084865Sobrien		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
29184865Sobrien		goto err2;
29284865Sobrien	}
29384865Sobrien	i = 0;
29484865Sobrien	while (gelf_getsym(data, i++, &sym) != NULL) {
29584865Sobrien		/*
29684865Sobrien		 * Calculate the address mapped to the virtual memory
29784865Sobrien		 * by rtld.
29884865Sobrien		 */
29984865Sobrien		if (ehdr.e_type != ET_EXEC)
30084865Sobrien			rsym = map->pr_vaddr + sym.st_value;
30184865Sobrien		else
30284865Sobrien			rsym = sym.st_value;
30384865Sobrien		if (addr >= rsym && addr < rsym + sym.st_size) {
30484865Sobrien			s = elf_strptr(e, symtabstridx, sym.st_name);
30584865Sobrien			if (s) {
306104834Sobrien				strlcpy(name, s, namesz);
307104834Sobrien				memcpy(symcopy, &sym, sizeof(sym));
30884865Sobrien				/*
30984865Sobrien				 * DTrace expects the st_value to contain
31084865Sobrien				 * only the address relative to the start of
31184865Sobrien				 * the function.
312130561Sobrien				 */
31384865Sobrien				symcopy->st_value = rsym;
31484865Sobrien				error = 0;
31584865Sobrien				goto out;
31684865Sobrien			}
31784865Sobrien		}
31884865Sobrien	}
31984865Sobrienout:
320130561Sobrienerr2:
32184865Sobrien	elf_end(e);
32284865Sobrienerr1:
32384865Sobrien	close(fd);
32484865Sobrienerr0:
325130561Sobrien	free(map);
32684865Sobrien	return (error);
32784865Sobrien}
32884865Sobrien
32984865Sobrienprmap_t *
330130561Sobrienproc_name2map(struct proc_handle *p, const char *name)
33184865Sobrien{
33284865Sobrien	size_t i;
33384865Sobrien	int cnt;
33484865Sobrien	prmap_t *map;
33589857Sobrien	char tmppath[MAXPATHLEN];
33684865Sobrien	struct kinfo_vmentry *kves, *kve;
33784865Sobrien	rd_loadobj_t *rdl;
33884865Sobrien
339130561Sobrien	/*
34084865Sobrien	 * If we haven't iterated over the list of loaded objects,
34184865Sobrien	 * librtld_db isn't yet initialized and it's very likely
34284865Sobrien	 * that librtld_db called us. We need to do the heavy
34384865Sobrien	 * lifting here to find the symbol librtld_db is looking for.
34489857Sobrien	 */
34584865Sobrien	if (p->nobjs == 0) {
34684865Sobrien		if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL)
34789857Sobrien			return (NULL);
34889857Sobrien		for (i = 0; i < (size_t)cnt; i++) {
34989857Sobrien			kve = kves + i;
35089857Sobrien			basename_r(kve->kve_path, tmppath);
35189857Sobrien			if (strcmp(tmppath, name) == 0) {
35284865Sobrien				map = proc_addr2map(p, kve->kve_start);
35384865Sobrien				free(kves);
35484865Sobrien				return (map);
35584865Sobrien			}
356130561Sobrien		}
357130561Sobrien		free(kves);
358130561Sobrien		return (NULL);
35984865Sobrien	}
36084865Sobrien	if (name == NULL || strcmp(name, "a.out") == 0) {
36184865Sobrien		map = proc_addr2map(p, p->rdobjs[0].rdl_saddr);
36284865Sobrien		return (map);
36384865Sobrien	}
36484865Sobrien	for (i = 0; i < p->nobjs; i++) {
365130561Sobrien		rdl = &p->rdobjs[i];
36684865Sobrien		basename_r(rdl->rdl_path, tmppath);
36784865Sobrien		if (strcmp(tmppath, name) == 0) {
36884865Sobrien			if ((map = malloc(sizeof(*map))) == NULL)
36989857Sobrien				return (NULL);
37089857Sobrien			proc_rdl2prmap(rdl, map);
37184865Sobrien			return (map);
37284865Sobrien		}
37389857Sobrien	}
37489857Sobrien
37589857Sobrien	return (NULL);
37689857Sobrien}
37789857Sobrien
37884865Sobrienint
37984865Sobrienproc_name2sym(struct proc_handle *p, const char *object, const char *symbol,
38084865Sobrien    GElf_Sym *symcopy)
38184865Sobrien{
38284865Sobrien	Elf *e;
383130561Sobrien	Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL;
384130561Sobrien	Elf_Data *data;
385130561Sobrien	GElf_Shdr shdr;
386130561Sobrien	GElf_Sym sym;
387130561Sobrien	GElf_Ehdr ehdr;
388130561Sobrien	int fd, error = -1;
389130561Sobrien	size_t i;
39084865Sobrien	prmap_t *map;
39189857Sobrien	char *s;
39284865Sobrien	unsigned long symtabstridx = 0, dynsymstridx = 0;
39384865Sobrien
39489857Sobrien	if ((map = proc_name2map(p, object)) == NULL) {
39589857Sobrien		DPRINTFX("ERROR: couldn't find object %s", object);
39684865Sobrien		goto err0;
39789857Sobrien	}
39889857Sobrien	if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) {
39984865Sobrien		DPRINTF("ERROR: open %s failed", map->pr_mapname);
40084865Sobrien		goto err0;
40189857Sobrien	}
40289857Sobrien	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
40389857Sobrien		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
40489857Sobrien		goto err1;
40589857Sobrien	}
40689857Sobrien	if (gelf_getehdr(e, &ehdr) == NULL) {
40789857Sobrien		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
40889857Sobrien		goto err2;
40989857Sobrien	}
41089857Sobrien	/*
41189857Sobrien	 * Find the index of the STRTAB and SYMTAB sections to locate
41289857Sobrien	 * symbol names.
41389857Sobrien	 */
41489857Sobrien	scn = NULL;
41589857Sobrien	while ((scn = elf_nextscn(e, scn)) != NULL) {
41684865Sobrien		gelf_getshdr(scn, &shdr);
41784865Sobrien		switch (shdr.sh_type) {
41884865Sobrien		case SHT_SYMTAB:
41984865Sobrien			symtabscn = scn;
42084865Sobrien			symtabstridx = shdr.sh_link;
42184865Sobrien			break;
42284865Sobrien		case SHT_DYNSYM:
42389857Sobrien			dynsymscn = scn;
42489857Sobrien			dynsymstridx = shdr.sh_link;
42589857Sobrien			break;
42684865Sobrien		default:
42784865Sobrien			break;
42884865Sobrien		}
42989857Sobrien	}
43089857Sobrien	/*
43189857Sobrien	 * Iterate over the Dynamic Symbols table to find the symbol.
43289857Sobrien	 * Then look up the string name in STRTAB (.dynstr)
43384865Sobrien	 */
43484865Sobrien	if ((data = elf_getdata(dynsymscn, NULL))) {
43584865Sobrien		i = 0;
43684865Sobrien		while (gelf_getsym(data, i++, &sym) != NULL) {
43784865Sobrien			s = elf_strptr(e, dynsymstridx, sym.st_name);
43884865Sobrien			if (s && strcmp(s, symbol) == 0) {
43984865Sobrien				memcpy(symcopy, &sym, sizeof(sym));
44089857Sobrien				symcopy->st_value = map->pr_vaddr + sym.st_value;
44189857Sobrien				error = 0;
44289857Sobrien				goto out;
44389857Sobrien			}
44489857Sobrien		}
44584865Sobrien	}
446130561Sobrien	/*
44784865Sobrien	 * Iterate over the Symbols Table to find the symbol.
44884865Sobrien	 * Then look up the string name in STRTAB (.dynstr)
44984865Sobrien	 */
45084865Sobrien	if (symtabscn == NULL)
45184865Sobrien		goto err2;
45284865Sobrien	if ((data = elf_getdata(symtabscn, NULL))) {
45384865Sobrien		i = 0;
45484865Sobrien		while (gelf_getsym(data, i++, &sym) != NULL) {
45584865Sobrien			s = elf_strptr(e, symtabstridx, sym.st_name);
45689857Sobrien			if (s && strcmp(s, symbol) == 0) {
45789857Sobrien				memcpy(symcopy, &sym, sizeof(sym));
45884865Sobrien				error = 0;
45984865Sobrien				goto out;
46084865Sobrien			}
46189857Sobrien		}
46289857Sobrien	}
46389857Sobrienout:
46489857Sobrienerr2:
46584865Sobrien	elf_end(e);
46684865Sobrienerr1:
46784865Sobrien	close(fd);
46884865Sobrienerr0:
46989857Sobrien	free(map);
47084865Sobrien
47189857Sobrien	return (error);
47284865Sobrien}
47389857Sobrien
47484865Sobrien
47589857Sobrienint
47684865Sobrienproc_iter_symbyaddr(struct proc_handle *p, const char *object, int which,
47784865Sobrien    int mask, proc_sym_f *func, void *cd)
47889857Sobrien{
47989857Sobrien	Elf *e;
48089857Sobrien	int i, fd;
48189857Sobrien	prmap_t *map;
48289857Sobrien	Elf_Scn *scn, *foundscn = NULL;
48389857Sobrien	Elf_Data *data;
48489857Sobrien	GElf_Shdr shdr;
48589857Sobrien	GElf_Sym sym;
48689857Sobrien	unsigned long stridx = -1;
48789857Sobrien	char *s;
48889857Sobrien	int error = -1;
48989857Sobrien
49089857Sobrien	if ((map = proc_name2map(p, object)) == NULL)
49189857Sobrien		return (-1);
49284865Sobrien	if ((fd = open(map->pr_mapname, O_RDONLY)) < 0) {
49384865Sobrien		DPRINTF("ERROR: open %s failed", map->pr_mapname);
49484865Sobrien		goto err0;
49589857Sobrien	}
496130561Sobrien	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
497130561Sobrien		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
49889857Sobrien		goto err1;
49984865Sobrien	}
50084865Sobrien	/*
50189857Sobrien	 * Find the section we are looking for.
50289857Sobrien	 */
50384865Sobrien	scn = NULL;
50484865Sobrien	while ((scn = elf_nextscn(e, scn)) != NULL) {
50584865Sobrien		gelf_getshdr(scn, &shdr);
50684865Sobrien		if (which == PR_SYMTAB &&
50789857Sobrien		    shdr.sh_type == SHT_SYMTAB) {
50884865Sobrien			foundscn = scn;
50984865Sobrien			break;
51084865Sobrien		} else if (which == PR_DYNSYM &&
51184865Sobrien		    shdr.sh_type == SHT_DYNSYM) {
512130561Sobrien			foundscn = scn;
51384865Sobrien			break;
51484865Sobrien		}
51589857Sobrien	}
51689857Sobrien	if (!foundscn)
51789857Sobrien		return (-1);
51889857Sobrien	stridx = shdr.sh_link;
51989857Sobrien	if ((data = elf_getdata(foundscn, NULL)) == NULL) {
52089857Sobrien		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
52189857Sobrien		goto err2;
52289857Sobrien	}
52389857Sobrien	i = 0;
52489857Sobrien	while (gelf_getsym(data, i++, &sym) != NULL) {
52589857Sobrien		if (GELF_ST_BIND(sym.st_info) == STB_LOCAL &&
52684865Sobrien		    (mask & BIND_LOCAL) == 0)
52789857Sobrien			continue;
52889857Sobrien		if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL &&
52989857Sobrien		    (mask & BIND_GLOBAL) == 0)
53089857Sobrien			continue;
53189857Sobrien		if (GELF_ST_BIND(sym.st_info) == STB_WEAK &&
53289857Sobrien		    (mask & BIND_WEAK) == 0)
53389857Sobrien			continue;
53489857Sobrien		if (GELF_ST_TYPE(sym.st_info) == STT_NOTYPE &&
53589857Sobrien		    (mask & TYPE_NOTYPE) == 0)
53689857Sobrien			continue;
53789857Sobrien		if (GELF_ST_TYPE(sym.st_info) == STT_OBJECT &&
53889857Sobrien		    (mask & TYPE_OBJECT) == 0)
53989857Sobrien			continue;
54089857Sobrien		if (GELF_ST_TYPE(sym.st_info) == STT_FUNC &&
54189857Sobrien		    (mask & TYPE_FUNC) == 0)
54289857Sobrien			continue;
54389857Sobrien		if (GELF_ST_TYPE(sym.st_info) == STT_SECTION &&
54489857Sobrien		    (mask & TYPE_SECTION) == 0)
54584865Sobrien			continue;
54689857Sobrien		if (GELF_ST_TYPE(sym.st_info) == STT_FILE &&
54789857Sobrien		    (mask & TYPE_FILE) == 0)
54889857Sobrien			continue;
54989857Sobrien		s = elf_strptr(e, stridx, sym.st_name);
55089857Sobrien		sym.st_value += map->pr_vaddr;
55189857Sobrien		(*func)(cd, &sym, s);
55289857Sobrien	}
55389857Sobrien	error = 0;
55489857Sobrienerr2:
55589857Sobrien	elf_end(e);
55689857Sobrienerr1:
55784865Sobrien	close(fd);
55884865Sobrienerr0:
55989857Sobrien	free(map);
56089857Sobrien	return (error);
56184865Sobrien}
56284865Sobrien