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