proc_sym.c revision 265074
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 265074 2014-04-29 03:37:30Z 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 symtab;
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		if (ehdr.e_type != ET_EXEC)
266			rsym = map->pr_vaddr + sym.st_value;
267		else
268			rsym = sym.st_value;
269		if (addr >= rsym && addr < rsym + sym.st_size) {
270			s = elf_strptr(e, dynsymstridx, sym.st_name);
271			if (s) {
272				strlcpy(name, s, namesz);
273				memcpy(symcopy, &sym, sizeof(sym));
274				/*
275				 * DTrace expects the st_value to contain
276				 * only the address relative to the start of
277				 * the function.
278				 */
279				symcopy->st_value = rsym;
280				error = 0;
281				goto out;
282			}
283		}
284	}
285symtab:
286	/*
287	 * Iterate over the Symbols Table to find the symbol.
288	 * Then look up the string name in STRTAB (.dynstr)
289	 */
290	if ((data = elf_getdata(symtabscn, NULL)) == NULL) {
291		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
292		goto err2;
293	}
294	i = 0;
295	while (gelf_getsym(data, i++, &sym) != NULL) {
296		/*
297		 * Calculate the address mapped to the virtual memory
298		 * by rtld.
299		 */
300		if (ehdr.e_type != ET_EXEC)
301			rsym = map->pr_vaddr + sym.st_value;
302		else
303			rsym = sym.st_value;
304		if (addr >= rsym && addr < rsym + sym.st_size) {
305			s = elf_strptr(e, symtabstridx, sym.st_name);
306			if (s) {
307				strlcpy(name, s, namesz);
308				memcpy(symcopy, &sym, sizeof(sym));
309				/*
310				 * DTrace expects the st_value to contain
311				 * only the address relative to the start of
312				 * the function.
313				 */
314				symcopy->st_value = rsym;
315				error = 0;
316				goto out;
317			}
318		}
319	}
320out:
321err2:
322	elf_end(e);
323err1:
324	close(fd);
325err0:
326	free(map);
327	return (error);
328}
329
330prmap_t *
331proc_name2map(struct proc_handle *p, const char *name)
332{
333	size_t i;
334	int cnt;
335	prmap_t *map;
336	char tmppath[MAXPATHLEN];
337	struct kinfo_vmentry *kves, *kve;
338	rd_loadobj_t *rdl;
339
340	/*
341	 * If we haven't iterated over the list of loaded objects,
342	 * librtld_db isn't yet initialized and it's very likely
343	 * that librtld_db called us. We need to do the heavy
344	 * lifting here to find the symbol librtld_db is looking for.
345	 */
346	if (p->nobjs == 0) {
347		if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL)
348			return (NULL);
349		for (i = 0; i < (size_t)cnt; i++) {
350			kve = kves + i;
351			basename_r(kve->kve_path, tmppath);
352			if (strcmp(tmppath, name) == 0) {
353				map = proc_addr2map(p, kve->kve_start);
354				free(kves);
355				return (map);
356			}
357		}
358		free(kves);
359		return (NULL);
360	}
361	if (name == NULL || strcmp(name, "a.out") == 0) {
362		map = proc_addr2map(p, p->rdobjs[0].rdl_saddr);
363		return (map);
364	}
365	for (i = 0; i < p->nobjs; i++) {
366		rdl = &p->rdobjs[i];
367		basename_r(rdl->rdl_path, tmppath);
368		if (strcmp(tmppath, name) == 0) {
369			if ((map = malloc(sizeof(*map))) == NULL)
370				return (NULL);
371			proc_rdl2prmap(rdl, map);
372			return (map);
373		}
374	}
375
376	return (NULL);
377}
378
379int
380proc_name2sym(struct proc_handle *p, const char *object, const char *symbol,
381    GElf_Sym *symcopy)
382{
383	Elf *e;
384	Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL;
385	Elf_Data *data;
386	GElf_Shdr shdr;
387	GElf_Sym sym;
388	GElf_Ehdr ehdr;
389	int fd, error = -1;
390	size_t i;
391	prmap_t *map;
392	char *s;
393	unsigned long symtabstridx = 0, dynsymstridx = 0;
394
395	if ((map = proc_name2map(p, object)) == NULL) {
396		DPRINTFX("ERROR: couldn't find object %s", object);
397		goto err0;
398	}
399	if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) {
400		DPRINTF("ERROR: open %s failed", map->pr_mapname);
401		goto err0;
402	}
403	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
404		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
405		goto err1;
406	}
407	if (gelf_getehdr(e, &ehdr) == NULL) {
408		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
409		goto err2;
410	}
411	/*
412	 * Find the index of the STRTAB and SYMTAB sections to locate
413	 * symbol names.
414	 */
415	scn = NULL;
416	while ((scn = elf_nextscn(e, scn)) != NULL) {
417		gelf_getshdr(scn, &shdr);
418		switch (shdr.sh_type) {
419		case SHT_SYMTAB:
420			symtabscn = scn;
421			symtabstridx = shdr.sh_link;
422			break;
423		case SHT_DYNSYM:
424			dynsymscn = scn;
425			dynsymstridx = shdr.sh_link;
426			break;
427		default:
428			break;
429		}
430	}
431	/*
432	 * Iterate over the Dynamic Symbols table to find the symbol.
433	 * Then look up the string name in STRTAB (.dynstr)
434	 */
435	if ((data = elf_getdata(dynsymscn, NULL))) {
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				if (ehdr.e_type != ET_EXEC)
442					symcopy->st_value += map->pr_vaddr;
443				error = 0;
444				goto out;
445			}
446		}
447	}
448	/*
449	 * Iterate over the Symbols Table to find the symbol.
450	 * Then look up the string name in STRTAB (.dynstr)
451	 */
452	if ((data = elf_getdata(symtabscn, NULL))) {
453		i = 0;
454		while (gelf_getsym(data, i++, &sym) != NULL) {
455			s = elf_strptr(e, symtabstridx, sym.st_name);
456			if (s && strcmp(s, symbol) == 0) {
457				memcpy(symcopy, &sym, sizeof(sym));
458				if (ehdr.e_type != ET_EXEC)
459					symcopy->st_value += map->pr_vaddr;
460				error = 0;
461				goto out;
462			}
463		}
464	}
465out:
466	DPRINTFX("found addr 0x%lx for %s", symcopy->st_value, symbol);
467err2:
468	elf_end(e);
469err1:
470	close(fd);
471err0:
472	free(map);
473
474	return (error);
475}
476
477
478int
479proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which,
480    int mask, proc_sym_f *func, void *cd)
481{
482	Elf *e;
483	int i, fd;
484	prmap_t *map;
485	Elf_Scn *scn, *foundscn = NULL;
486	Elf_Data *data;
487	GElf_Ehdr ehdr;
488	GElf_Shdr shdr;
489	GElf_Sym sym;
490	unsigned long stridx = -1;
491	char *s;
492	int error = -1;
493
494	if ((map = proc_name2map(p, object)) == NULL)
495		return (-1);
496	if ((fd = open(map->pr_mapname, O_RDONLY)) < 0) {
497		DPRINTF("ERROR: open %s failed", map->pr_mapname);
498		goto err0;
499	}
500	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
501		DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1));
502		goto err1;
503	}
504	if (gelf_getehdr(e, &ehdr) == NULL) {
505		DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
506		goto err2;
507	}
508	/*
509	 * Find the section we are looking for.
510	 */
511	scn = NULL;
512	while ((scn = elf_nextscn(e, scn)) != NULL) {
513		gelf_getshdr(scn, &shdr);
514		if (which == PR_SYMTAB &&
515		    shdr.sh_type == SHT_SYMTAB) {
516			foundscn = scn;
517			break;
518		} else if (which == PR_DYNSYM &&
519		    shdr.sh_type == SHT_DYNSYM) {
520			foundscn = scn;
521			break;
522		}
523	}
524	if (!foundscn)
525		return (-1);
526	stridx = shdr.sh_link;
527	if ((data = elf_getdata(foundscn, NULL)) == NULL) {
528		DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
529		goto err2;
530	}
531	i = 0;
532	while (gelf_getsym(data, i++, &sym) != NULL) {
533		if (GELF_ST_BIND(sym.st_info) == STB_LOCAL &&
534		    (mask & BIND_LOCAL) == 0)
535			continue;
536		if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL &&
537		    (mask & BIND_GLOBAL) == 0)
538			continue;
539		if (GELF_ST_BIND(sym.st_info) == STB_WEAK &&
540		    (mask & BIND_WEAK) == 0)
541			continue;
542		if (GELF_ST_TYPE(sym.st_info) == STT_NOTYPE &&
543		    (mask & TYPE_NOTYPE) == 0)
544			continue;
545		if (GELF_ST_TYPE(sym.st_info) == STT_OBJECT &&
546		    (mask & TYPE_OBJECT) == 0)
547			continue;
548		if (GELF_ST_TYPE(sym.st_info) == STT_FUNC &&
549		    (mask & TYPE_FUNC) == 0)
550			continue;
551		if (GELF_ST_TYPE(sym.st_info) == STT_SECTION &&
552		    (mask & TYPE_SECTION) == 0)
553			continue;
554		if (GELF_ST_TYPE(sym.st_info) == STT_FILE &&
555		    (mask & TYPE_FILE) == 0)
556			continue;
557		s = elf_strptr(e, stridx, sym.st_name);
558		if (ehdr.e_type != ET_EXEC)
559			sym.st_value += map->pr_vaddr;
560		(*func)(cd, &sym, s);
561	}
562	error = 0;
563err2:
564	elf_end(e);
565err1:
566	close(fd);
567err0:
568	free(map);
569	return (error);
570}
571