]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - lib/libproc/proc_sym.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / lib / libproc / proc_sym.c
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 #ifndef NO_CXA_DEMANGLE
50 extern char *__cxa_demangle(const char *, char *, size_t *, int *);
51 #endif /* NO_CXA_DEMANGLE */
52
53 static void     proc_rdl2prmap(rd_loadobj_t *, prmap_t *);
54
55 static void
56 demangle(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         }
69 fail:
70 #endif /* NO_CXA_DEMANGLE */
71         strlcpy(buf, symbol, len);
72 }
73
74 static int
75 find_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
89 static void
90 proc_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
106 char *
107 proc_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
123 prmap_t *
124 proc_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
152 int
153 proc_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
183 prmap_t *
184 proc_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
246 int
247 proc_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         }
331 symtab:
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         }
366 out:
367 err2:
368         elf_end(e);
369 err1:
370         close(fd);
371 err0:
372         free(map);
373         return (error);
374 }
375
376 prmap_t *
377 proc_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
426 int
427 proc_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         }
512 out:
513         DPRINTFX("found addr 0x%lx for %s", symcopy->st_value, symbol);
514 err2:
515         elf_end(e);
516 err1:
517         close(fd);
518 err0:
519         free(map);
520
521         return (error);
522 }
523
524
525 int
526 proc_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;
610 err2:
611         elf_end(e);
612 err1:
613         close(fd);
614 err0:
615         free(map);
616         return (error);
617 }