xref: /freebsd/lib/libproc/proc_sym.c (revision ad30f8e79bd1007cc2476e491bd21b4f5e389e0a)
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$
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 static void	proc_rdl2prmap(rd_loadobj_t *, prmap_t *);
50 
51 static void
52 proc_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 
68 char *
69 proc_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 
85 prmap_t *
86 proc_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 
106 int
107 proc_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 
137 prmap_t *
138 proc_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 
200 int
201 proc_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 (!map->pr_mapname || (fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) {
220 		warn("ERROR: open %s failed", map->pr_mapname);
221 		goto err0;
222 	}
223 	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
224 		warn("ERROR: elf_begin() failed");
225 		goto err1;
226 	}
227 	if (gelf_getehdr(e, &ehdr) == NULL) {
228 		warn("ERROR: gelf_getehdr() failed");
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 		DPRINTF("ERROR: elf_getdata() failed");
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 		DPRINTF("ERROR: elf_getdata() failed");
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 	}
318 out:
319 err2:
320 	elf_end(e);
321 err1:
322 	close(fd);
323 err0:
324 	free(map);
325 	return (error);
326 }
327 
328 prmap_t *
329 proc_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 
377 int
378 proc_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 		DPRINTF("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 		warn("ERROR: elf_begin() failed");
403 		goto err1;
404 	}
405 	if (gelf_getehdr(e, &ehdr) == NULL) {
406 		warn("ERROR: gelf_getehdr() failed");
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 		DPRINTF("ERROR: elf_getdata() failed");
435 		goto err2;
436 	}
437 	i = 0;
438 	while (gelf_getsym(data, i++, &sym) != NULL) {
439 		s = elf_strptr(e, dynsymstridx, sym.st_name);
440 		if (s && strcmp(s, symbol) == 0) {
441 			memcpy(symcopy, &sym, sizeof(sym));
442 			symcopy->st_value = map->pr_vaddr + sym.st_value;
443 			error = 0;
444 			goto out;
445 		}
446 	}
447 	/*
448 	 * Iterate over the Symbols Table to find the symbol.
449 	 * Then look up the string name in STRTAB (.dynstr)
450 	 */
451 	if (symtabscn == NULL)
452 		goto err2;
453 	if ((data = elf_getdata(symtabscn, NULL)) == NULL) {
454 		DPRINTF("ERROR: elf_getdata() failed");
455 		goto err2;
456 	}
457 	i = 0;
458 	while (gelf_getsym(data, i++, &sym) != NULL) {
459 		s = elf_strptr(e, symtabstridx, sym.st_name);
460 		if (s && strcmp(s, symbol) == 0) {
461 			memcpy(symcopy, &sym, sizeof(sym));
462 			error = 0;
463 			goto out;
464 		}
465 	}
466 out:
467 err2:
468 	elf_end(e);
469 err1:
470 	close(fd);
471 err0:
472 	free(map);
473 
474 	return (error);
475 }
476 
477 
478 int
479 proc_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_Shdr shdr;
488 	GElf_Sym sym;
489 	unsigned long stridx = -1;
490 	char *s;
491 	int error = -1;
492 
493 	if ((map = proc_name2map(p, object)) == NULL)
494 		return (-1);
495 	if ((fd = open(map->pr_mapname, O_RDONLY)) < 0) {
496 		warn("ERROR: open %s failed", map->pr_mapname);
497 		goto err0;
498 	}
499 	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
500 		warn("ERROR: elf_begin() failed");
501 		goto err1;
502 	}
503 	/*
504 	 * Find the section we are looking for.
505 	 */
506 	scn = NULL;
507 	while ((scn = elf_nextscn(e, scn)) != NULL) {
508 		gelf_getshdr(scn, &shdr);
509 		if (which == PR_SYMTAB &&
510 		    shdr.sh_type == SHT_SYMTAB) {
511 			foundscn = scn;
512 			break;
513 		} else if (which == PR_DYNSYM &&
514 		    shdr.sh_type == SHT_DYNSYM) {
515 			foundscn = scn;
516 			break;
517 		}
518 	}
519 	if (!foundscn)
520 		return (-1);
521 	stridx = shdr.sh_link;
522 	if ((data = elf_getdata(foundscn, NULL)) == NULL) {
523 		DPRINTF("ERROR: elf_getdata() failed");
524 		goto err2;
525 	}
526 	i = 0;
527 	while (gelf_getsym(data, i++, &sym) != NULL) {
528 		if (GELF_ST_BIND(sym.st_info) == STB_LOCAL &&
529 		    (mask & BIND_LOCAL) == 0)
530 			continue;
531 		if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL &&
532 		    (mask & BIND_GLOBAL) == 0)
533 			continue;
534 		if (GELF_ST_BIND(sym.st_info) == STB_WEAK &&
535 		    (mask & BIND_WEAK) == 0)
536 			continue;
537 		if (GELF_ST_TYPE(sym.st_info) == STT_NOTYPE &&
538 		    (mask & TYPE_NOTYPE) == 0)
539 			continue;
540 		if (GELF_ST_TYPE(sym.st_info) == STT_OBJECT &&
541 		    (mask & TYPE_OBJECT) == 0)
542 			continue;
543 		if (GELF_ST_TYPE(sym.st_info) == STT_FUNC &&
544 		    (mask & TYPE_FUNC) == 0)
545 			continue;
546 		if (GELF_ST_TYPE(sym.st_info) == STT_SECTION &&
547 		    (mask & TYPE_SECTION) == 0)
548 			continue;
549 		if (GELF_ST_TYPE(sym.st_info) == STT_FILE &&
550 		    (mask & TYPE_FILE) == 0)
551 			continue;
552 		s = elf_strptr(e, stridx, sym.st_name);
553 		sym.st_value += map->pr_vaddr;
554 		(*func)(cd, &sym, s);
555 	}
556 	error = 0;
557 err2:
558 	elf_end(e);
559 err1:
560 	close(fd);
561 err0:
562 	free(map);
563 	return (error);
564 }
565