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