proc_sym.c revision 259894
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/9/lib/libproc/proc_sym.c 259894 2013-12-25 22:32:52Z markj $
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
49static void	proc_rdl2prmap(rd_loadobj_t *, prmap_t *);
50
51static void
52proc_rdl2prmap(rd_loadobj_t *rdl, prmap_t *map)
53{
54	map->pr_vaddr = rdl->rdl_saddr;
55	map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr;
56	map->pr_offset = rdl->rdl_offset;
57	map->pr_mflags = 0;
58	if (rdl->rdl_prot & RD_RDL_R)
59		map->pr_mflags |= MA_READ;
60	if (rdl->rdl_prot & RD_RDL_W)
61		map->pr_mflags |= MA_WRITE;
62	if (rdl->rdl_prot & RD_RDL_X)
63		map->pr_mflags |= MA_EXEC;
64	strlcpy(map->pr_mapname, rdl->rdl_path,
65	    sizeof(map->pr_mapname));
66}
67
68char *
69proc_objname(struct proc_handle *p, uintptr_t addr, char *objname,
70    size_t objnamesz)
71{
72	size_t i;
73	rd_loadobj_t *rdl;
74
75	for (i = 0; i < p->nobjs; i++) {
76		rdl = &p->rdobjs[i];
77		if (addr >= rdl->rdl_saddr && addr <= rdl->rdl_eaddr) {
78			strlcpy(objname, rdl->rdl_path, objnamesz);
79			return (objname);
80		}
81	}
82	return (NULL);
83}
84
85prmap_t *
86proc_obj2map(struct proc_handle *p, const char *objname)
87{
88	size_t i;
89	prmap_t *map;
90	rd_loadobj_t *rdl;
91	char path[MAXPATHLEN];
92
93	for (i = 0; i < p->nobjs; i++) {
94		rdl = &p->rdobjs[i];
95		basename_r(rdl->rdl_path, path);
96		if (strcmp(path, objname) == 0) {
97			if ((map = malloc(sizeof(*map))) == NULL)
98				return (NULL);
99			proc_rdl2prmap(rdl, map);
100			return (map);
101		}
102	}
103	return (NULL);
104}
105
106int
107proc_iter_objs(struct proc_handle *p, proc_map_f *func, void *cd)
108{
109	size_t i;
110	rd_loadobj_t *rdl;
111	prmap_t map;
112	char path[MAXPATHLEN];
113	char last[MAXPATHLEN];
114
115	if (p->nobjs == 0)
116		return (-1);
117	memset(last, 0, sizeof(last));
118	for (i = 0; i < p->nobjs; i++) {
119		rdl = &p->rdobjs[i];
120		proc_rdl2prmap(rdl, &map);
121		basename_r(rdl->rdl_path, path);
122		/*
123		 * We shouldn't call the callback twice with the same object.
124		 * To do that we are assuming the fact that if there are
125		 * repeated object names (i.e. different mappings for the
126		 * same object) they occur next to each other.
127		 */
128		if (strcmp(path, last) == 0)
129			continue;
130		(*func)(cd, &map, path);
131		strlcpy(last, path, sizeof(last));
132	}
133
134	return (0);
135}
136
137prmap_t *
138proc_addr2map(struct proc_handle *p, uintptr_t addr)
139{
140	size_t i;
141	int cnt, lastvn = 0;
142	prmap_t *map;
143	rd_loadobj_t *rdl;
144	struct kinfo_vmentry *kves, *kve;
145
146	/*
147	 * If we don't have a cache of listed objects, we need to query
148	 * it ourselves.
149	 */
150	if (p->nobjs == 0) {
151		if ((kves = kinfo_getvmmap(p->pid, &cnt)) == NULL)
152			return (NULL);
153		for (i = 0; i < (size_t)cnt; i++) {
154			kve = kves + i;
155			if (kve->kve_type == KVME_TYPE_VNODE)
156				lastvn = i;
157			if (addr >= kve->kve_start && addr <= kve->kve_end) {
158				if ((map = malloc(sizeof(*map))) == NULL) {
159					free(kves);
160					return (NULL);
161				}
162				map->pr_vaddr = kve->kve_start;
163				map->pr_size = kve->kve_end - kve->kve_start;
164				map->pr_offset = kve->kve_offset;
165				map->pr_mflags = 0;
166				if (kve->kve_protection & KVME_PROT_READ)
167					map->pr_mflags |= MA_READ;
168				if (kve->kve_protection & KVME_PROT_WRITE)
169					map->pr_mflags |= MA_WRITE;
170				if (kve->kve_protection & KVME_PROT_EXEC)
171					map->pr_mflags |= MA_EXEC;
172				if (kve->kve_flags & KVME_FLAG_COW)
173					map->pr_mflags |= MA_COW;
174				if (kve->kve_flags & KVME_FLAG_NEEDS_COPY)
175					map->pr_mflags |= MA_NEEDS_COPY;
176				if (kve->kve_flags & KVME_FLAG_NOCOREDUMP)
177					map->pr_mflags |= MA_NOCOREDUMP;
178				strlcpy(map->pr_mapname, kves[lastvn].kve_path,
179				    sizeof(map->pr_mapname));
180				free(kves);
181				return (map);
182			}
183		}
184		free(kves);
185		return (NULL);
186	}
187
188	for (i = 0; i < p->nobjs; i++) {
189		rdl = &p->rdobjs[i];
190		if (addr >= rdl->rdl_saddr && addr <= rdl->rdl_eaddr) {
191			if ((map = malloc(sizeof(*map))) == NULL)
192				return (NULL);
193			proc_rdl2prmap(rdl, map);
194			return (map);
195		}
196	}
197	return (NULL);
198}
199
200int
201proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name,
202    size_t namesz, GElf_Sym *symcopy)
203{
204	Elf *e;
205	Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL;
206	Elf_Data *data;
207	GElf_Shdr shdr;
208	GElf_Sym sym;
209	GElf_Ehdr ehdr;
210	int fd, error = -1;
211	size_t i;
212	uint64_t rsym;
213	prmap_t *map;
214	char *s;
215	unsigned long symtabstridx = 0, dynsymstridx = 0;
216
217	if ((map = proc_addr2map(p, addr)) == NULL)
218		return (-1);
219	if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) {
220		DPRINTF("ERROR: open %s failed", map->pr_mapname);
221		goto err0;
222	}
223	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
224		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
225		goto err1;
226	}
227	if (gelf_getehdr(e, &ehdr) == NULL) {
228		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
229		goto err2;
230	}
231	/*
232	 * Find the index of the STRTAB and SYMTAB sections to locate
233	 * symbol names.
234	 */
235	scn = NULL;
236	while ((scn = elf_nextscn(e, scn)) != NULL) {
237		gelf_getshdr(scn, &shdr);
238		switch (shdr.sh_type) {
239		case SHT_SYMTAB:
240			symtabscn = scn;
241			symtabstridx = shdr.sh_link;
242			break;
243		case SHT_DYNSYM:
244			dynsymscn = scn;
245			dynsymstridx = shdr.sh_link;
246			break;
247		default:
248			break;
249		}
250	}
251	/*
252	 * Iterate over the Dynamic Symbols table to find the symbol.
253	 * Then look up the string name in STRTAB (.dynstr)
254	 */
255	if ((data = elf_getdata(dynsymscn, NULL)) == NULL) {
256		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
257		goto err2;
258	}
259	i = 0;
260	while (gelf_getsym(data, i++, &sym) != NULL) {
261		/*
262		 * Calculate the address mapped to the virtual memory
263		 * by rtld.
264		 */
265		rsym = map->pr_vaddr + sym.st_value;
266		if (addr >= rsym && addr <= (rsym + sym.st_size)) {
267			s = elf_strptr(e, dynsymstridx, sym.st_name);
268			if (s) {
269				strlcpy(name, s, namesz);
270				memcpy(symcopy, &sym, sizeof(sym));
271				/*
272				 * DTrace expects the st_value to contain
273				 * only the address relative to the start of
274				 * the function.
275				 */
276				symcopy->st_value = rsym;
277				error = 0;
278				goto out;
279			}
280		}
281	}
282	/*
283	 * Iterate over the Symbols Table to find the symbol.
284	 * Then look up the string name in STRTAB (.dynstr)
285	 */
286	if (symtabscn == NULL)
287		goto err2;
288	if ((data = elf_getdata(symtabscn, NULL)) == NULL) {
289		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
290		goto err2;
291	}
292	i = 0;
293	while (gelf_getsym(data, i++, &sym) != NULL) {
294		/*
295		 * Calculate the address mapped to the virtual memory
296		 * by rtld.
297		 */
298		if (ehdr.e_type != ET_EXEC)
299			rsym = map->pr_vaddr + sym.st_value;
300		else
301			rsym = sym.st_value;
302		if (addr >= rsym && addr <= (rsym + sym.st_size)) {
303			s = elf_strptr(e, symtabstridx, sym.st_name);
304			if (s) {
305				strlcpy(name, s, namesz);
306				memcpy(symcopy, &sym, sizeof(sym));
307				/*
308				 * DTrace expects the st_value to contain
309				 * only the address relative to the start of
310				 * the function.
311				 */
312				symcopy->st_value = rsym;
313				error = 0;
314				goto out;
315			}
316		}
317	}
318out:
319err2:
320	elf_end(e);
321err1:
322	close(fd);
323err0:
324	free(map);
325	return (error);
326}
327
328prmap_t *
329proc_name2map(struct proc_handle *p, const char *name)
330{
331	size_t i;
332	int cnt;
333	prmap_t *map;
334	char tmppath[MAXPATHLEN];
335	struct kinfo_vmentry *kves, *kve;
336	rd_loadobj_t *rdl;
337
338	/*
339	 * If we haven't iterated over the list of loaded objects,
340	 * librtld_db isn't yet initialized and it's very likely
341	 * that librtld_db called us. We need to do the heavy
342	 * lifting here to find the symbol librtld_db is looking for.
343	 */
344	if (p->nobjs == 0) {
345		if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL)
346			return (NULL);
347		for (i = 0; i < (size_t)cnt; i++) {
348			kve = kves + i;
349			basename_r(kve->kve_path, tmppath);
350			if (strcmp(tmppath, name) == 0) {
351				map = proc_addr2map(p, kve->kve_start);
352				free(kves);
353				return (map);
354			}
355		}
356		free(kves);
357		return (NULL);
358	}
359	if (name == NULL || strcmp(name, "a.out") == 0) {
360		map = proc_addr2map(p, p->rdobjs[0].rdl_saddr);
361		return (map);
362	}
363	for (i = 0; i < p->nobjs; i++) {
364		rdl = &p->rdobjs[i];
365		basename_r(rdl->rdl_path, tmppath);
366		if (strcmp(tmppath, name) == 0) {
367			if ((map = malloc(sizeof(*map))) == NULL)
368				return (NULL);
369			proc_rdl2prmap(rdl, map);
370			return (map);
371		}
372	}
373
374	return (NULL);
375}
376
377int
378proc_name2sym(struct proc_handle *p, const char *object, const char *symbol,
379    GElf_Sym *symcopy)
380{
381	Elf *e;
382	Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL;
383	Elf_Data *data;
384	GElf_Shdr shdr;
385	GElf_Sym sym;
386	GElf_Ehdr ehdr;
387	int fd, error = -1;
388	size_t i;
389	prmap_t *map;
390	char *s;
391	unsigned long symtabstridx = 0, dynsymstridx = 0;
392
393	if ((map = proc_name2map(p, object)) == NULL) {
394		DPRINTFX("ERROR: couldn't find object %s", object);
395		goto err0;
396	}
397	if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) {
398		DPRINTF("ERROR: open %s failed", map->pr_mapname);
399		goto err0;
400	}
401	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
402		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
403		goto err1;
404	}
405	if (gelf_getehdr(e, &ehdr) == NULL) {
406		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
407		goto err2;
408	}
409	/*
410	 * Find the index of the STRTAB and SYMTAB sections to locate
411	 * symbol names.
412	 */
413	scn = NULL;
414	while ((scn = elf_nextscn(e, scn)) != NULL) {
415		gelf_getshdr(scn, &shdr);
416		switch (shdr.sh_type) {
417		case SHT_SYMTAB:
418			symtabscn = scn;
419			symtabstridx = shdr.sh_link;
420			break;
421		case SHT_DYNSYM:
422			dynsymscn = scn;
423			dynsymstridx = shdr.sh_link;
424			break;
425		default:
426			break;
427		}
428	}
429	/*
430	 * Iterate over the Dynamic Symbols table to find the symbol.
431	 * Then look up the string name in STRTAB (.dynstr)
432	 */
433	if ((data = elf_getdata(dynsymscn, NULL)) == NULL) {
434		goto err2;
435	}
436	i = 0;
437	while (gelf_getsym(data, i++, &sym) != NULL) {
438		s = elf_strptr(e, dynsymstridx, sym.st_name);
439		if (s && strcmp(s, symbol) == 0) {
440			memcpy(symcopy, &sym, sizeof(sym));
441			symcopy->st_value = map->pr_vaddr + sym.st_value;
442			error = 0;
443			goto out;
444		}
445	}
446	/*
447	 * Iterate over the Symbols Table to find the symbol.
448	 * Then look up the string name in STRTAB (.dynstr)
449	 */
450	if (symtabscn == NULL)
451		goto err2;
452	if ((data = elf_getdata(symtabscn, NULL)) == NULL) {
453		DPRINTF("ERROR: elf_getdata() failed");
454		goto err2;
455	}
456	i = 0;
457	while (gelf_getsym(data, i++, &sym) != NULL) {
458		s = elf_strptr(e, symtabstridx, sym.st_name);
459		if (s && strcmp(s, symbol) == 0) {
460			memcpy(symcopy, &sym, sizeof(sym));
461			error = 0;
462			goto out;
463		}
464	}
465out:
466err2:
467	elf_end(e);
468err1:
469	close(fd);
470err0:
471	free(map);
472
473	return (error);
474}
475
476
477int
478proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which,
479    int mask, proc_sym_f *func, void *cd)
480{
481	Elf *e;
482	int i, fd;
483	prmap_t *map;
484	Elf_Scn *scn, *foundscn = NULL;
485	Elf_Data *data;
486	GElf_Shdr shdr;
487	GElf_Sym sym;
488	unsigned long stridx = -1;
489	char *s;
490	int error = -1;
491
492	if ((map = proc_name2map(p, object)) == NULL)
493		return (-1);
494	if ((fd = open(map->pr_mapname, O_RDONLY)) < 0) {
495		DPRINTF("ERROR: open %s failed", map->pr_mapname);
496		goto err0;
497	}
498	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
499		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
500		goto err1;
501	}
502	/*
503	 * Find the section we are looking for.
504	 */
505	scn = NULL;
506	while ((scn = elf_nextscn(e, scn)) != NULL) {
507		gelf_getshdr(scn, &shdr);
508		if (which == PR_SYMTAB &&
509		    shdr.sh_type == SHT_SYMTAB) {
510			foundscn = scn;
511			break;
512		} else if (which == PR_DYNSYM &&
513		    shdr.sh_type == SHT_DYNSYM) {
514			foundscn = scn;
515			break;
516		}
517	}
518	if (!foundscn)
519		return (-1);
520	stridx = shdr.sh_link;
521	if ((data = elf_getdata(foundscn, NULL)) == NULL) {
522		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
523		goto err2;
524	}
525	i = 0;
526	while (gelf_getsym(data, i++, &sym) != NULL) {
527		if (GELF_ST_BIND(sym.st_info) == STB_LOCAL &&
528		    (mask & BIND_LOCAL) == 0)
529			continue;
530		if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL &&
531		    (mask & BIND_GLOBAL) == 0)
532			continue;
533		if (GELF_ST_BIND(sym.st_info) == STB_WEAK &&
534		    (mask & BIND_WEAK) == 0)
535			continue;
536		if (GELF_ST_TYPE(sym.st_info) == STT_NOTYPE &&
537		    (mask & TYPE_NOTYPE) == 0)
538			continue;
539		if (GELF_ST_TYPE(sym.st_info) == STT_OBJECT &&
540		    (mask & TYPE_OBJECT) == 0)
541			continue;
542		if (GELF_ST_TYPE(sym.st_info) == STT_FUNC &&
543		    (mask & TYPE_FUNC) == 0)
544			continue;
545		if (GELF_ST_TYPE(sym.st_info) == STT_SECTION &&
546		    (mask & TYPE_SECTION) == 0)
547			continue;
548		if (GELF_ST_TYPE(sym.st_info) == STT_FILE &&
549		    (mask & TYPE_FILE) == 0)
550			continue;
551		s = elf_strptr(e, stridx, sym.st_name);
552		sym.st_value += map->pr_vaddr;
553		(*func)(cd, &sym, s);
554	}
555	error = 0;
556err2:
557	elf_end(e);
558err1:
559	close(fd);
560err0:
561	free(map);
562	return (error);
563}
564