xref: /netbsd-src/external/bsd/libproc/dist/proc_sym.c (revision 1cdaec48cfc2fbd088d0a89e0a0d350829f17d14)
1fbcd1dd1Schristos /*-
2fbcd1dd1Schristos  * Copyright (c) 2010 The FreeBSD Foundation
3fbcd1dd1Schristos  * Copyright (c) 2008 John Birrell (jb@freebsd.org)
4fbcd1dd1Schristos  * All rights reserved.
5fbcd1dd1Schristos  *
6fbcd1dd1Schristos  * Portions of this software were developed by Rui Paulo under sponsorship
7fbcd1dd1Schristos  * from the FreeBSD Foundation.
8fbcd1dd1Schristos  *
9fbcd1dd1Schristos  * Redistribution and use in source and binary forms, with or without
10fbcd1dd1Schristos  * modification, are permitted provided that the following conditions
11fbcd1dd1Schristos  * are met:
12fbcd1dd1Schristos  * 1. Redistributions of source code must retain the above copyright
13fbcd1dd1Schristos  *    notice, this list of conditions and the following disclaimer.
14fbcd1dd1Schristos  * 2. Redistributions in binary form must reproduce the above copyright
15fbcd1dd1Schristos  *    notice, this list of conditions and the following disclaimer in the
16fbcd1dd1Schristos  *    documentation and/or other materials provided with the distribution.
17fbcd1dd1Schristos  *
18fbcd1dd1Schristos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19fbcd1dd1Schristos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20fbcd1dd1Schristos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21fbcd1dd1Schristos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22fbcd1dd1Schristos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23fbcd1dd1Schristos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24fbcd1dd1Schristos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25fbcd1dd1Schristos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26fbcd1dd1Schristos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27fbcd1dd1Schristos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28fbcd1dd1Schristos  * SUCH DAMAGE.
29fbcd1dd1Schristos  */
30fbcd1dd1Schristos 
31fbcd1dd1Schristos #include <sys/cdefs.h>
3252ebe1c2Schristos #ifdef __FBSDID
33fbcd1dd1Schristos __FBSDID("$FreeBSD: head/lib/libproc/proc_sym.c 279946 2015-03-13 04:26:48Z stas $");
3452ebe1c2Schristos #else
35*1cdaec48Srin __RCSID("$NetBSD: proc_sym.c,v 1.6 2024/07/05 09:43:53 rin Exp $");
3652ebe1c2Schristos #endif
37fbcd1dd1Schristos 
38fbcd1dd1Schristos #include <sys/types.h>
39fbcd1dd1Schristos #ifndef NO_CTF
40fbcd1dd1Schristos #include <sys/ctf.h>
41fbcd1dd1Schristos #include <sys/ctf_api.h>
42fbcd1dd1Schristos #endif
43be56a00dSkamil #if defined(__FreeBSD__)
44fbcd1dd1Schristos #include <sys/user.h>
45be56a00dSkamil #endif
4652ebe1c2Schristos #include <sys/sysctl.h>
47fbcd1dd1Schristos 
48fbcd1dd1Schristos #include <assert.h>
49fbcd1dd1Schristos #include <err.h>
50fbcd1dd1Schristos #include <fcntl.h>
51fbcd1dd1Schristos #include <libgen.h>
52fbcd1dd1Schristos #include <stdio.h>
53fbcd1dd1Schristos #include <stdlib.h>
54fbcd1dd1Schristos #include <string.h>
55fbcd1dd1Schristos #include <unistd.h>
56fbcd1dd1Schristos #ifndef NO_CTF
57fbcd1dd1Schristos #include <libctf.h>
58fbcd1dd1Schristos #endif
5952ebe1c2Schristos #include <util.h>
60fbcd1dd1Schristos 
61fbcd1dd1Schristos #include "_libproc.h"
62fbcd1dd1Schristos 
63d22d8342Schs #define DBG_PATH_FMT "/usr/libdata/debug/%s.debug"
64d22d8342Schs 
65fbcd1dd1Schristos #ifdef NO_CTF
66fbcd1dd1Schristos typedef struct ctf_file ctf_file_t;
67fbcd1dd1Schristos #endif
68fbcd1dd1Schristos 
69fbcd1dd1Schristos #ifndef NO_CXA_DEMANGLE
70fbcd1dd1Schristos extern char *__cxa_demangle(const char *, char *, size_t *, int *);
71fbcd1dd1Schristos #endif /* NO_CXA_DEMANGLE */
72fbcd1dd1Schristos 
73fbcd1dd1Schristos static void	proc_rdl2prmap(rd_loadobj_t *, prmap_t *);
74fbcd1dd1Schristos 
7552ebe1c2Schristos #ifdef __NetBSD__
basename_r(const char * path,char * buf)7652ebe1c2Schristos static char *basename_r(const char *path, char *buf)
7752ebe1c2Schristos {
7852ebe1c2Schristos 	// We "know" this works.
7952ebe1c2Schristos 	if (path[0])
8052ebe1c2Schristos 		strlcpy(buf, strrchr(path, '/') + 1, PATH_MAX);
8152ebe1c2Schristos 	else
8252ebe1c2Schristos 		buf[0] = '\0';
8352ebe1c2Schristos 	return buf;
8452ebe1c2Schristos }
8552ebe1c2Schristos #endif
8652ebe1c2Schristos 
87fbcd1dd1Schristos static void
demangle(const char * symbol,char * buf,size_t len)88fbcd1dd1Schristos demangle(const char *symbol, char *buf, size_t len)
89fbcd1dd1Schristos {
90fbcd1dd1Schristos #ifndef NO_CXA_DEMANGLE
91fbcd1dd1Schristos 	char *dembuf;
92fbcd1dd1Schristos 
93fbcd1dd1Schristos 	if (symbol[0] == '_' && symbol[1] == 'Z' && symbol[2]) {
94fbcd1dd1Schristos 		dembuf = __cxa_demangle(symbol, NULL, NULL, NULL);
95fbcd1dd1Schristos 		if (!dembuf)
96fbcd1dd1Schristos 			goto fail;
97fbcd1dd1Schristos 		strlcpy(buf, dembuf, len);
98fbcd1dd1Schristos 		free(dembuf);
99fbcd1dd1Schristos 		return;
100fbcd1dd1Schristos 	}
101fbcd1dd1Schristos fail:
102fbcd1dd1Schristos #endif /* NO_CXA_DEMANGLE */
103fbcd1dd1Schristos 	strlcpy(buf, symbol, len);
104fbcd1dd1Schristos }
105fbcd1dd1Schristos 
106fbcd1dd1Schristos static int
find_dbg_obj(const char * path)107fbcd1dd1Schristos find_dbg_obj(const char *path)
108fbcd1dd1Schristos {
109fbcd1dd1Schristos 	int fd;
110fbcd1dd1Schristos 	char dbg_path[PATH_MAX];
111fbcd1dd1Schristos 
112d22d8342Schs 	snprintf(dbg_path, sizeof(dbg_path), DBG_PATH_FMT, path);
113fbcd1dd1Schristos 	fd = open(dbg_path, O_RDONLY);
114fbcd1dd1Schristos 	if (fd >= 0)
115fbcd1dd1Schristos 		return (fd);
116fbcd1dd1Schristos 	else
117fbcd1dd1Schristos 		return (open(path, O_RDONLY));
118fbcd1dd1Schristos }
119fbcd1dd1Schristos 
120fbcd1dd1Schristos static void
proc_rdl2prmap(rd_loadobj_t * rdl,prmap_t * map)121fbcd1dd1Schristos proc_rdl2prmap(rd_loadobj_t *rdl, prmap_t *map)
122fbcd1dd1Schristos {
123fbcd1dd1Schristos 	map->pr_vaddr = rdl->rdl_saddr;
124fbcd1dd1Schristos 	map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr;
125fbcd1dd1Schristos 	map->pr_offset = rdl->rdl_offset;
126fbcd1dd1Schristos 	map->pr_mflags = 0;
127fbcd1dd1Schristos 	if (rdl->rdl_prot & RD_RDL_R)
128fbcd1dd1Schristos 		map->pr_mflags |= MA_READ;
129fbcd1dd1Schristos 	if (rdl->rdl_prot & RD_RDL_W)
130fbcd1dd1Schristos 		map->pr_mflags |= MA_WRITE;
131fbcd1dd1Schristos 	if (rdl->rdl_prot & RD_RDL_X)
132fbcd1dd1Schristos 		map->pr_mflags |= MA_EXEC;
133fbcd1dd1Schristos 	strlcpy(map->pr_mapname, rdl->rdl_path,
134fbcd1dd1Schristos 	    sizeof(map->pr_mapname));
135fbcd1dd1Schristos }
136fbcd1dd1Schristos 
137fbcd1dd1Schristos char *
proc_objname(struct proc_handle * p,uintptr_t addr,char * objname,size_t objnamesz)138fbcd1dd1Schristos proc_objname(struct proc_handle *p, uintptr_t addr, char *objname,
139fbcd1dd1Schristos     size_t objnamesz)
140fbcd1dd1Schristos {
141fbcd1dd1Schristos 	size_t i;
142fbcd1dd1Schristos 	rd_loadobj_t *rdl;
143fbcd1dd1Schristos 
144c9f38035Sprlw1 	if (p->nobjs == 0)
145c9f38035Sprlw1 		if (proc_rdagent(p) == NULL)
146c9f38035Sprlw1 			return (NULL);
147c9f38035Sprlw1 
148fbcd1dd1Schristos 	for (i = 0; i < p->nobjs; i++) {
149fbcd1dd1Schristos 		rdl = &p->rdobjs[i];
150fbcd1dd1Schristos 		if (addr >= rdl->rdl_saddr && addr < rdl->rdl_eaddr) {
151fbcd1dd1Schristos 			strlcpy(objname, rdl->rdl_path, objnamesz);
152fbcd1dd1Schristos 			return (objname);
153fbcd1dd1Schristos 		}
154fbcd1dd1Schristos 	}
155fbcd1dd1Schristos 	return (NULL);
156fbcd1dd1Schristos }
157fbcd1dd1Schristos 
158fbcd1dd1Schristos prmap_t *
proc_obj2map(struct proc_handle * p,const char * objname)159fbcd1dd1Schristos proc_obj2map(struct proc_handle *p, const char *objname)
160fbcd1dd1Schristos {
161fbcd1dd1Schristos 	size_t i;
162fbcd1dd1Schristos 	prmap_t *map;
163fbcd1dd1Schristos 	rd_loadobj_t *rdl;
164fbcd1dd1Schristos 	char path[MAXPATHLEN];
165fbcd1dd1Schristos 
166fbcd1dd1Schristos 	rdl = NULL;
167fbcd1dd1Schristos 	for (i = 0; i < p->nobjs; i++) {
168fbcd1dd1Schristos 		basename_r(p->rdobjs[i].rdl_path, path);
169fbcd1dd1Schristos 		if (strcmp(path, objname) == 0) {
170fbcd1dd1Schristos 			rdl = &p->rdobjs[i];
171fbcd1dd1Schristos 			break;
172fbcd1dd1Schristos 		}
173fbcd1dd1Schristos 	}
174fbcd1dd1Schristos 	if (rdl == NULL) {
175fbcd1dd1Schristos 		if (strcmp(objname, "a.out") == 0 && p->rdexec != NULL)
176fbcd1dd1Schristos 			rdl = p->rdexec;
177fbcd1dd1Schristos 		else
178fbcd1dd1Schristos 			return (NULL);
179fbcd1dd1Schristos 	}
180fbcd1dd1Schristos 
181fbcd1dd1Schristos 	if ((map = malloc(sizeof(*map))) == NULL)
182fbcd1dd1Schristos 		return (NULL);
183fbcd1dd1Schristos 	proc_rdl2prmap(rdl, map);
184fbcd1dd1Schristos 	return (map);
185fbcd1dd1Schristos }
186fbcd1dd1Schristos 
187fbcd1dd1Schristos int
proc_iter_objs(struct proc_handle * p,proc_map_f * func,void * cd)188fbcd1dd1Schristos proc_iter_objs(struct proc_handle *p, proc_map_f *func, void *cd)
189fbcd1dd1Schristos {
190fbcd1dd1Schristos 	size_t i;
191fbcd1dd1Schristos 	rd_loadobj_t *rdl;
192fbcd1dd1Schristos 	prmap_t map;
193fbcd1dd1Schristos 	char path[MAXPATHLEN];
194fbcd1dd1Schristos 	char last[MAXPATHLEN];
195fbcd1dd1Schristos 	int error;
196fbcd1dd1Schristos 
197fbcd1dd1Schristos 	if (p->nobjs == 0)
198fbcd1dd1Schristos 		return (-1);
199fbcd1dd1Schristos 
200fbcd1dd1Schristos 	error = 0;
201fbcd1dd1Schristos 	memset(last, 0, sizeof(last));
202fbcd1dd1Schristos 	for (i = 0; i < p->nobjs; i++) {
203fbcd1dd1Schristos 		rdl = &p->rdobjs[i];
204fbcd1dd1Schristos 		proc_rdl2prmap(rdl, &map);
205fbcd1dd1Schristos 		basename_r(rdl->rdl_path, path);
206fbcd1dd1Schristos 		/*
207fbcd1dd1Schristos 		 * We shouldn't call the callback twice with the same object.
208fbcd1dd1Schristos 		 * To do that we are assuming the fact that if there are
209fbcd1dd1Schristos 		 * repeated object names (i.e. different mappings for the
210fbcd1dd1Schristos 		 * same object) they occur next to each other.
211fbcd1dd1Schristos 		 */
212fbcd1dd1Schristos 		if (strcmp(path, last) == 0)
213fbcd1dd1Schristos 			continue;
214fbcd1dd1Schristos 		if ((error = (*func)(cd, &map, path)) != 0)
215fbcd1dd1Schristos 			break;
216fbcd1dd1Schristos 		strlcpy(last, path, sizeof(last));
217fbcd1dd1Schristos 	}
218fbcd1dd1Schristos 	return (error);
219fbcd1dd1Schristos }
220fbcd1dd1Schristos 
221fbcd1dd1Schristos prmap_t *
proc_addr2map(struct proc_handle * p,uintptr_t addr)222fbcd1dd1Schristos proc_addr2map(struct proc_handle *p, uintptr_t addr)
223fbcd1dd1Schristos {
22452ebe1c2Schristos 	size_t i, cnt, lastvn = 0;
225fbcd1dd1Schristos 	prmap_t *map;
226fbcd1dd1Schristos 	rd_loadobj_t *rdl;
227fbcd1dd1Schristos 	struct kinfo_vmentry *kves, *kve;
228fbcd1dd1Schristos 
229fbcd1dd1Schristos 	/*
230fbcd1dd1Schristos 	 * If we don't have a cache of listed objects, we need to query
231fbcd1dd1Schristos 	 * it ourselves.
232fbcd1dd1Schristos 	 */
233fbcd1dd1Schristos 	if (p->nobjs == 0) {
234fbcd1dd1Schristos 		if ((kves = kinfo_getvmmap(p->pid, &cnt)) == NULL)
235fbcd1dd1Schristos 			return (NULL);
236fbcd1dd1Schristos 		for (i = 0; i < (size_t)cnt; i++) {
237fbcd1dd1Schristos 			kve = kves + i;
238fbcd1dd1Schristos 			if (kve->kve_type == KVME_TYPE_VNODE)
239fbcd1dd1Schristos 				lastvn = i;
240fbcd1dd1Schristos 			if (addr >= kve->kve_start && addr < kve->kve_end) {
241fbcd1dd1Schristos 				if ((map = malloc(sizeof(*map))) == NULL) {
242fbcd1dd1Schristos 					free(kves);
243fbcd1dd1Schristos 					return (NULL);
244fbcd1dd1Schristos 				}
245fbcd1dd1Schristos 				map->pr_vaddr = kve->kve_start;
246fbcd1dd1Schristos 				map->pr_size = kve->kve_end - kve->kve_start;
247fbcd1dd1Schristos 				map->pr_offset = kve->kve_offset;
248fbcd1dd1Schristos 				map->pr_mflags = 0;
249fbcd1dd1Schristos 				if (kve->kve_protection & KVME_PROT_READ)
250fbcd1dd1Schristos 					map->pr_mflags |= MA_READ;
251fbcd1dd1Schristos 				if (kve->kve_protection & KVME_PROT_WRITE)
252fbcd1dd1Schristos 					map->pr_mflags |= MA_WRITE;
253fbcd1dd1Schristos 				if (kve->kve_protection & KVME_PROT_EXEC)
254fbcd1dd1Schristos 					map->pr_mflags |= MA_EXEC;
255fbcd1dd1Schristos 				if (kve->kve_flags & KVME_FLAG_COW)
256fbcd1dd1Schristos 					map->pr_mflags |= MA_COW;
257fbcd1dd1Schristos 				if (kve->kve_flags & KVME_FLAG_NEEDS_COPY)
258fbcd1dd1Schristos 					map->pr_mflags |= MA_NEEDS_COPY;
259fbcd1dd1Schristos 				if (kve->kve_flags & KVME_FLAG_NOCOREDUMP)
260fbcd1dd1Schristos 					map->pr_mflags |= MA_NOCOREDUMP;
261fbcd1dd1Schristos 				strlcpy(map->pr_mapname, kves[lastvn].kve_path,
262fbcd1dd1Schristos 				    sizeof(map->pr_mapname));
263fbcd1dd1Schristos 				free(kves);
264fbcd1dd1Schristos 				return (map);
265fbcd1dd1Schristos 			}
266fbcd1dd1Schristos 		}
267fbcd1dd1Schristos 		free(kves);
268fbcd1dd1Schristos 		return (NULL);
269fbcd1dd1Schristos 	}
270fbcd1dd1Schristos 
271fbcd1dd1Schristos 	for (i = 0; i < p->nobjs; i++) {
272fbcd1dd1Schristos 		rdl = &p->rdobjs[i];
273fbcd1dd1Schristos 		if (addr >= rdl->rdl_saddr && addr < rdl->rdl_eaddr) {
274fbcd1dd1Schristos 			if ((map = malloc(sizeof(*map))) == NULL)
275fbcd1dd1Schristos 				return (NULL);
276fbcd1dd1Schristos 			proc_rdl2prmap(rdl, map);
277fbcd1dd1Schristos 			return (map);
278fbcd1dd1Schristos 		}
279fbcd1dd1Schristos 	}
280fbcd1dd1Schristos 	return (NULL);
281fbcd1dd1Schristos }
282fbcd1dd1Schristos 
283fbcd1dd1Schristos /*
284fbcd1dd1Schristos  * Look up the symbol at addr, returning a copy of the symbol and its name.
285fbcd1dd1Schristos  */
286fbcd1dd1Schristos static int
lookup_addr(Elf * e,Elf_Scn * scn,u_long stridx,uintptr_t off,uintptr_t addr,const char ** name,GElf_Sym * symcopy)287fbcd1dd1Schristos lookup_addr(Elf *e, Elf_Scn *scn, u_long stridx, uintptr_t off, uintptr_t addr,
288fbcd1dd1Schristos     const char **name, GElf_Sym *symcopy)
289fbcd1dd1Schristos {
290fbcd1dd1Schristos 	GElf_Sym sym;
291fbcd1dd1Schristos 	Elf_Data *data;
292fbcd1dd1Schristos 	const char *s;
293fbcd1dd1Schristos 	uint64_t rsym;
294fbcd1dd1Schristos 	int i;
295fbcd1dd1Schristos 
296fbcd1dd1Schristos 	if ((data = elf_getdata(scn, NULL)) == NULL) {
297fbcd1dd1Schristos 		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
298fbcd1dd1Schristos 		return (1);
299fbcd1dd1Schristos 	}
300fbcd1dd1Schristos 	for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) {
301fbcd1dd1Schristos 		rsym = off + sym.st_value;
302fbcd1dd1Schristos 		if (addr >= rsym && addr < rsym + sym.st_size) {
303fbcd1dd1Schristos 			s = elf_strptr(e, stridx, sym.st_name);
304fbcd1dd1Schristos 			if (s != NULL) {
305fbcd1dd1Schristos 				*name = s;
306fbcd1dd1Schristos 				memcpy(symcopy, &sym, sizeof(*symcopy));
307fbcd1dd1Schristos 				/*
308fbcd1dd1Schristos 				 * DTrace expects the st_value to contain
309fbcd1dd1Schristos 				 * only the address relative to the start of
310fbcd1dd1Schristos 				 * the function.
311fbcd1dd1Schristos 				 */
312fbcd1dd1Schristos 				symcopy->st_value = rsym;
313fbcd1dd1Schristos 				return (0);
314fbcd1dd1Schristos 			}
315fbcd1dd1Schristos 		}
316fbcd1dd1Schristos 	}
317fbcd1dd1Schristos 	return (1);
318fbcd1dd1Schristos }
319fbcd1dd1Schristos 
320fbcd1dd1Schristos int
proc_addr2sym(struct proc_handle * p,uintptr_t addr,char * name,size_t namesz,GElf_Sym * symcopy)321fbcd1dd1Schristos proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name,
322fbcd1dd1Schristos     size_t namesz, GElf_Sym *symcopy)
323fbcd1dd1Schristos {
324fbcd1dd1Schristos 	GElf_Ehdr ehdr;
325fbcd1dd1Schristos 	GElf_Shdr shdr;
326fbcd1dd1Schristos 	Elf *e;
327fbcd1dd1Schristos 	Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL;
328fbcd1dd1Schristos 	prmap_t *map;
329fbcd1dd1Schristos 	const char *s;
330fbcd1dd1Schristos 	uintptr_t off;
331fbcd1dd1Schristos 	u_long symtabstridx = 0, dynsymstridx = 0;
332fbcd1dd1Schristos 	int fd, error = -1;
333fbcd1dd1Schristos 
334fbcd1dd1Schristos 	if ((map = proc_addr2map(p, addr)) == NULL)
335fbcd1dd1Schristos 		return (-1);
336fbcd1dd1Schristos 	if ((fd = find_dbg_obj(map->pr_mapname)) < 0) {
337fbcd1dd1Schristos 		DPRINTF("ERROR: open %s failed", map->pr_mapname);
338fbcd1dd1Schristos 		goto err0;
339fbcd1dd1Schristos 	}
340fbcd1dd1Schristos 	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
341fbcd1dd1Schristos 		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
342fbcd1dd1Schristos 		goto err1;
343fbcd1dd1Schristos 	}
344fbcd1dd1Schristos 	if (gelf_getehdr(e, &ehdr) == NULL) {
345fbcd1dd1Schristos 		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
346fbcd1dd1Schristos 		goto err2;
347fbcd1dd1Schristos 	}
348fbcd1dd1Schristos 
349fbcd1dd1Schristos 	/*
350fbcd1dd1Schristos 	 * Find the index of the STRTAB and SYMTAB sections to locate
351fbcd1dd1Schristos 	 * symbol names.
352fbcd1dd1Schristos 	 */
353fbcd1dd1Schristos 	scn = NULL;
354fbcd1dd1Schristos 	while ((scn = elf_nextscn(e, scn)) != NULL) {
355fbcd1dd1Schristos 		gelf_getshdr(scn, &shdr);
356fbcd1dd1Schristos 		switch (shdr.sh_type) {
357fbcd1dd1Schristos 		case SHT_SYMTAB:
358fbcd1dd1Schristos 			symtabscn = scn;
359fbcd1dd1Schristos 			symtabstridx = shdr.sh_link;
360fbcd1dd1Schristos 			break;
361fbcd1dd1Schristos 		case SHT_DYNSYM:
362fbcd1dd1Schristos 			dynsymscn = scn;
363fbcd1dd1Schristos 			dynsymstridx = shdr.sh_link;
364fbcd1dd1Schristos 			break;
365fbcd1dd1Schristos 		}
366fbcd1dd1Schristos 	}
367fbcd1dd1Schristos 
368fbcd1dd1Schristos 	off = ehdr.e_type == ET_EXEC ? 0 : map->pr_vaddr;
369fbcd1dd1Schristos 
370fbcd1dd1Schristos 	/*
371fbcd1dd1Schristos 	 * First look up the symbol in the dynsymtab, and fall back to the
372fbcd1dd1Schristos 	 * symtab if the lookup fails.
373fbcd1dd1Schristos 	 */
374d22d8342Schs 	if (dynsymscn) {
375fbcd1dd1Schristos 		error = lookup_addr(e, dynsymscn, dynsymstridx, off, addr, &s, symcopy);
376fbcd1dd1Schristos 		if (error == 0)
377fbcd1dd1Schristos 			goto out;
378d22d8342Schs 	}
379fbcd1dd1Schristos 
380fbcd1dd1Schristos 	error = lookup_addr(e, symtabscn, symtabstridx, off, addr, &s, symcopy);
381fbcd1dd1Schristos 	if (error != 0)
382fbcd1dd1Schristos 		goto err2;
383fbcd1dd1Schristos 
384fbcd1dd1Schristos out:
385fbcd1dd1Schristos 	demangle(s, name, namesz);
386fbcd1dd1Schristos err2:
387fbcd1dd1Schristos 	elf_end(e);
388fbcd1dd1Schristos err1:
389fbcd1dd1Schristos 	close(fd);
390fbcd1dd1Schristos err0:
391fbcd1dd1Schristos 	free(map);
392fbcd1dd1Schristos 	return (error);
393fbcd1dd1Schristos }
394fbcd1dd1Schristos 
395fbcd1dd1Schristos prmap_t *
proc_name2map(struct proc_handle * p,const char * name)396fbcd1dd1Schristos proc_name2map(struct proc_handle *p, const char *name)
397fbcd1dd1Schristos {
39852ebe1c2Schristos 	size_t i, cnt;
399fbcd1dd1Schristos 	prmap_t *map = NULL;
400fbcd1dd1Schristos 	char tmppath[MAXPATHLEN];
401fbcd1dd1Schristos 	struct kinfo_vmentry *kves, *kve;
402fbcd1dd1Schristos 	rd_loadobj_t *rdl;
403fbcd1dd1Schristos 
404fbcd1dd1Schristos 	/*
405fbcd1dd1Schristos 	 * If we haven't iterated over the list of loaded objects,
406fbcd1dd1Schristos 	 * librtld_db isn't yet initialized and it's very likely
407fbcd1dd1Schristos 	 * that librtld_db called us. We need to do the heavy
408fbcd1dd1Schristos 	 * lifting here to find the symbol librtld_db is looking for.
409fbcd1dd1Schristos 	 */
410fbcd1dd1Schristos 	if (p->nobjs == 0) {
411fbcd1dd1Schristos 		if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL)
412fbcd1dd1Schristos 			return (NULL);
413fbcd1dd1Schristos 		for (i = 0; i < (size_t)cnt; i++) {
414fbcd1dd1Schristos 			kve = kves + i;
415fbcd1dd1Schristos 			basename_r(kve->kve_path, tmppath);
416fbcd1dd1Schristos 			if (strcmp(tmppath, name) == 0) {
417fbcd1dd1Schristos 				map = proc_addr2map(p, kve->kve_start);
418fbcd1dd1Schristos 				break;
419fbcd1dd1Schristos 			}
420fbcd1dd1Schristos 		}
421fbcd1dd1Schristos 		free(kves);
422fbcd1dd1Schristos 	} else
423fbcd1dd1Schristos 		for (i = 0; i < p->nobjs; i++) {
424fbcd1dd1Schristos 			rdl = &p->rdobjs[i];
425fbcd1dd1Schristos 			basename_r(rdl->rdl_path, tmppath);
426fbcd1dd1Schristos 			if (strcmp(tmppath, name) == 0) {
427fbcd1dd1Schristos 				if ((map = malloc(sizeof(*map))) == NULL)
428fbcd1dd1Schristos 					return (NULL);
429fbcd1dd1Schristos 				proc_rdl2prmap(rdl, map);
430fbcd1dd1Schristos 				break;
431fbcd1dd1Schristos 			}
432fbcd1dd1Schristos 		}
433fbcd1dd1Schristos 
434fbcd1dd1Schristos 	if (map == NULL && strcmp(name, "a.out") == 0 && p->rdexec != NULL)
435fbcd1dd1Schristos 		map = proc_addr2map(p, p->rdexec->rdl_saddr);
436fbcd1dd1Schristos 
437fbcd1dd1Schristos 	return (map);
438fbcd1dd1Schristos }
439fbcd1dd1Schristos 
440fbcd1dd1Schristos /*
441fbcd1dd1Schristos  * Look up the symbol with the given name and return a copy of it.
442fbcd1dd1Schristos  */
443fbcd1dd1Schristos static int
lookup_name(Elf * e,Elf_Scn * scn,u_long stridx,const char * symbol,GElf_Sym * symcopy,prsyminfo_t * si)444fbcd1dd1Schristos lookup_name(Elf *e, Elf_Scn *scn, u_long stridx, const char *symbol,
445fbcd1dd1Schristos     GElf_Sym *symcopy, prsyminfo_t *si)
446fbcd1dd1Schristos {
447fbcd1dd1Schristos 	GElf_Sym sym;
448fbcd1dd1Schristos 	Elf_Data *data;
449fbcd1dd1Schristos 	char *s;
450fbcd1dd1Schristos 	int i;
451fbcd1dd1Schristos 
452fbcd1dd1Schristos 	if ((data = elf_getdata(scn, NULL)) == NULL) {
453fbcd1dd1Schristos 		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
454fbcd1dd1Schristos 		return (1);
455fbcd1dd1Schristos 	}
456fbcd1dd1Schristos 	for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) {
457fbcd1dd1Schristos 		s = elf_strptr(e, stridx, sym.st_name);
458fbcd1dd1Schristos 		if (s != NULL && strcmp(s, symbol) == 0) {
459fbcd1dd1Schristos 			memcpy(symcopy, &sym, sizeof(*symcopy));
460fbcd1dd1Schristos 			if (si != NULL)
461fbcd1dd1Schristos 				si->prs_id = i;
462fbcd1dd1Schristos 			return (0);
463fbcd1dd1Schristos 		}
464fbcd1dd1Schristos 	}
465fbcd1dd1Schristos 	return (1);
466fbcd1dd1Schristos }
467fbcd1dd1Schristos 
468fbcd1dd1Schristos int
proc_name2sym(struct proc_handle * p,const char * object,const char * symbol,GElf_Sym * symcopy,prsyminfo_t * si)469fbcd1dd1Schristos proc_name2sym(struct proc_handle *p, const char *object, const char *symbol,
470fbcd1dd1Schristos     GElf_Sym *symcopy, prsyminfo_t *si)
471fbcd1dd1Schristos {
472fbcd1dd1Schristos 	Elf *e;
473fbcd1dd1Schristos 	Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL;
474fbcd1dd1Schristos 	GElf_Shdr shdr;
475fbcd1dd1Schristos 	GElf_Ehdr ehdr;
476fbcd1dd1Schristos 	prmap_t *map;
477fbcd1dd1Schristos 	uintptr_t off;
478fbcd1dd1Schristos 	u_long symtabstridx = 0, dynsymstridx = 0;
479fbcd1dd1Schristos 	int fd, error = -1;
480fbcd1dd1Schristos 
481fbcd1dd1Schristos 	if ((map = proc_name2map(p, object)) == NULL) {
482fbcd1dd1Schristos 		DPRINTFX("ERROR: couldn't find object %s", object);
483fbcd1dd1Schristos 		goto err0;
484fbcd1dd1Schristos 	}
485fbcd1dd1Schristos 	if ((fd = find_dbg_obj(map->pr_mapname)) < 0) {
486fbcd1dd1Schristos 		DPRINTF("ERROR: open %s failed", map->pr_mapname);
487fbcd1dd1Schristos 		goto err0;
488fbcd1dd1Schristos 	}
489fbcd1dd1Schristos 	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
490fbcd1dd1Schristos 		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
491fbcd1dd1Schristos 		goto err1;
492fbcd1dd1Schristos 	}
493fbcd1dd1Schristos 	if (gelf_getehdr(e, &ehdr) == NULL) {
494fbcd1dd1Schristos 		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
495fbcd1dd1Schristos 		goto err2;
496fbcd1dd1Schristos 	}
497fbcd1dd1Schristos 	/*
498fbcd1dd1Schristos 	 * Find the index of the STRTAB and SYMTAB sections to locate
499fbcd1dd1Schristos 	 * symbol names.
500fbcd1dd1Schristos 	 */
501fbcd1dd1Schristos 	scn = NULL;
502fbcd1dd1Schristos 	while ((scn = elf_nextscn(e, scn)) != NULL) {
503fbcd1dd1Schristos 		gelf_getshdr(scn, &shdr);
504fbcd1dd1Schristos 		switch (shdr.sh_type) {
505fbcd1dd1Schristos 		case SHT_SYMTAB:
506fbcd1dd1Schristos 			symtabscn = scn;
507fbcd1dd1Schristos 			symtabstridx = shdr.sh_link;
508fbcd1dd1Schristos 			break;
509fbcd1dd1Schristos 		case SHT_DYNSYM:
510fbcd1dd1Schristos 			dynsymscn = scn;
511fbcd1dd1Schristos 			dynsymstridx = shdr.sh_link;
512fbcd1dd1Schristos 			break;
513fbcd1dd1Schristos 		}
514fbcd1dd1Schristos 	}
515fbcd1dd1Schristos 
516fbcd1dd1Schristos 	/*
517fbcd1dd1Schristos 	 * First look up the symbol in the dynsymtab, and fall back to the
518fbcd1dd1Schristos 	 * symtab if the lookup fails.
519fbcd1dd1Schristos 	 */
520d22d8342Schs 	if (dynsymscn) {
521fbcd1dd1Schristos 		error = lookup_name(e, dynsymscn, dynsymstridx, symbol, symcopy, si);
522fbcd1dd1Schristos 		if (error == 0)
523fbcd1dd1Schristos 			goto out;
524d22d8342Schs 	}
525fbcd1dd1Schristos 
526fbcd1dd1Schristos 	error = lookup_name(e, symtabscn, symtabstridx, symbol, symcopy, si);
527fbcd1dd1Schristos 	if (error == 0)
528fbcd1dd1Schristos 		goto out;
529fbcd1dd1Schristos 
530fbcd1dd1Schristos out:
531fbcd1dd1Schristos 	off = ehdr.e_type == ET_EXEC ? 0 : map->pr_vaddr;
532fbcd1dd1Schristos 	symcopy->st_value += off;
533fbcd1dd1Schristos 
534fbcd1dd1Schristos err2:
535fbcd1dd1Schristos 	elf_end(e);
536fbcd1dd1Schristos err1:
537fbcd1dd1Schristos 	close(fd);
538fbcd1dd1Schristos err0:
539fbcd1dd1Schristos 	free(map);
540fbcd1dd1Schristos 
541fbcd1dd1Schristos 	return (error);
542fbcd1dd1Schristos }
543fbcd1dd1Schristos 
544fbcd1dd1Schristos ctf_file_t *
proc_name2ctf(struct proc_handle * p,const char * name)545fbcd1dd1Schristos proc_name2ctf(struct proc_handle *p, const char *name)
546fbcd1dd1Schristos {
547fbcd1dd1Schristos #ifndef NO_CTF
548fbcd1dd1Schristos 	ctf_file_t *ctf;
549fbcd1dd1Schristos 	prmap_t *map;
550fbcd1dd1Schristos 	int error;
551fbcd1dd1Schristos 
552fbcd1dd1Schristos 	if ((map = proc_name2map(p, name)) == NULL)
553fbcd1dd1Schristos 		return (NULL);
554fbcd1dd1Schristos 
555fbcd1dd1Schristos 	ctf = ctf_open(map->pr_mapname, &error);
556fbcd1dd1Schristos 	free(map);
557fbcd1dd1Schristos 	return (ctf);
558fbcd1dd1Schristos #else
559fbcd1dd1Schristos 	(void)p;
560fbcd1dd1Schristos 	(void)name;
561fbcd1dd1Schristos 	return (NULL);
562fbcd1dd1Schristos #endif
563fbcd1dd1Schristos }
564fbcd1dd1Schristos 
565fbcd1dd1Schristos int
proc_iter_symbyaddr(struct proc_handle * p,const char * object,int which,int mask,proc_sym_f * func,void * cd)566fbcd1dd1Schristos proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which,
567fbcd1dd1Schristos     int mask, proc_sym_f *func, void *cd)
568fbcd1dd1Schristos {
569fbcd1dd1Schristos 	Elf *e;
570fbcd1dd1Schristos 	int i, fd;
571fbcd1dd1Schristos 	prmap_t *map;
572fbcd1dd1Schristos 	Elf_Scn *scn, *foundscn = NULL;
573fbcd1dd1Schristos 	Elf_Data *data;
574fbcd1dd1Schristos 	GElf_Ehdr ehdr;
575fbcd1dd1Schristos 	GElf_Shdr shdr;
576fbcd1dd1Schristos 	GElf_Sym sym;
577fbcd1dd1Schristos 	unsigned long stridx = -1;
578fbcd1dd1Schristos 	char *s;
579fbcd1dd1Schristos 	int error = -1;
580fbcd1dd1Schristos 
581fbcd1dd1Schristos 	if ((map = proc_name2map(p, object)) == NULL)
582fbcd1dd1Schristos 		return (-1);
583fbcd1dd1Schristos 	if ((fd = find_dbg_obj(map->pr_mapname)) < 0) {
584fbcd1dd1Schristos 		DPRINTF("ERROR: open %s failed", map->pr_mapname);
585fbcd1dd1Schristos 		goto err0;
586fbcd1dd1Schristos 	}
587fbcd1dd1Schristos 	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
588fbcd1dd1Schristos 		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
589fbcd1dd1Schristos 		goto err1;
590fbcd1dd1Schristos 	}
591fbcd1dd1Schristos 	if (gelf_getehdr(e, &ehdr) == NULL) {
592fbcd1dd1Schristos 		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
593fbcd1dd1Schristos 		goto err2;
594fbcd1dd1Schristos 	}
595fbcd1dd1Schristos 	/*
596fbcd1dd1Schristos 	 * Find the section we are looking for.
597fbcd1dd1Schristos 	 */
598fbcd1dd1Schristos 	scn = NULL;
599fbcd1dd1Schristos 	while ((scn = elf_nextscn(e, scn)) != NULL) {
600fbcd1dd1Schristos 		gelf_getshdr(scn, &shdr);
601fbcd1dd1Schristos 		if (which == PR_SYMTAB &&
602fbcd1dd1Schristos 		    shdr.sh_type == SHT_SYMTAB) {
603fbcd1dd1Schristos 			foundscn = scn;
604fbcd1dd1Schristos 			break;
605fbcd1dd1Schristos 		} else if (which == PR_DYNSYM &&
606fbcd1dd1Schristos 		    shdr.sh_type == SHT_DYNSYM) {
607fbcd1dd1Schristos 			foundscn = scn;
608fbcd1dd1Schristos 			break;
609fbcd1dd1Schristos 		}
610fbcd1dd1Schristos 	}
611fbcd1dd1Schristos 	if (!foundscn)
612fbcd1dd1Schristos 		return (-1);
613fbcd1dd1Schristos 	stridx = shdr.sh_link;
614fbcd1dd1Schristos 	if ((data = elf_getdata(foundscn, NULL)) == NULL) {
615fbcd1dd1Schristos 		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
616fbcd1dd1Schristos 		goto err2;
617fbcd1dd1Schristos 	}
618fbcd1dd1Schristos 	for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) {
619fbcd1dd1Schristos 		if (GELF_ST_BIND(sym.st_info) == STB_LOCAL &&
620fbcd1dd1Schristos 		    (mask & BIND_LOCAL) == 0)
621fbcd1dd1Schristos 			continue;
622fbcd1dd1Schristos 		if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL &&
623fbcd1dd1Schristos 		    (mask & BIND_GLOBAL) == 0)
624fbcd1dd1Schristos 			continue;
625fbcd1dd1Schristos 		if (GELF_ST_BIND(sym.st_info) == STB_WEAK &&
626fbcd1dd1Schristos 		    (mask & BIND_WEAK) == 0)
627fbcd1dd1Schristos 			continue;
628fbcd1dd1Schristos 		if (GELF_ST_TYPE(sym.st_info) == STT_NOTYPE &&
629fbcd1dd1Schristos 		    (mask & TYPE_NOTYPE) == 0)
630fbcd1dd1Schristos 			continue;
631fbcd1dd1Schristos 		if (GELF_ST_TYPE(sym.st_info) == STT_OBJECT &&
632fbcd1dd1Schristos 		    (mask & TYPE_OBJECT) == 0)
633fbcd1dd1Schristos 			continue;
634fbcd1dd1Schristos 		if (GELF_ST_TYPE(sym.st_info) == STT_FUNC &&
635fbcd1dd1Schristos 		    (mask & TYPE_FUNC) == 0)
636fbcd1dd1Schristos 			continue;
637fbcd1dd1Schristos 		if (GELF_ST_TYPE(sym.st_info) == STT_SECTION &&
638fbcd1dd1Schristos 		    (mask & TYPE_SECTION) == 0)
639fbcd1dd1Schristos 			continue;
640fbcd1dd1Schristos 		if (GELF_ST_TYPE(sym.st_info) == STT_FILE &&
641fbcd1dd1Schristos 		    (mask & TYPE_FILE) == 0)
642fbcd1dd1Schristos 			continue;
643fbcd1dd1Schristos 		s = elf_strptr(e, stridx, sym.st_name);
644fbcd1dd1Schristos 		if (ehdr.e_type != ET_EXEC)
645fbcd1dd1Schristos 			sym.st_value += map->pr_vaddr;
646fbcd1dd1Schristos 		if ((error = (*func)(cd, &sym, s)) != 0)
647fbcd1dd1Schristos 			goto err2;
648fbcd1dd1Schristos 	}
649fbcd1dd1Schristos 	error = 0;
650fbcd1dd1Schristos err2:
651fbcd1dd1Schristos 	elf_end(e);
652fbcd1dd1Schristos err1:
653fbcd1dd1Schristos 	close(fd);
654fbcd1dd1Schristos err0:
655fbcd1dd1Schristos 	free(map);
656fbcd1dd1Schristos 	return (error);
657fbcd1dd1Schristos }
658