diff options
Diffstat (limited to 'lib/libproc/proc_sym.c')
-rw-r--r-- | lib/libproc/proc_sym.c | 509 |
1 files changed, 497 insertions, 12 deletions
diff --git a/lib/libproc/proc_sym.c b/lib/libproc/proc_sym.c index c17c515..82c4c74 100644 --- a/lib/libproc/proc_sym.c +++ b/lib/libproc/proc_sym.c @@ -1,7 +1,11 @@ /*- + * Copyright (c) 2010 The FreeBSD Foundation * Copyright (c) 2008 John Birrell (jb@freebsd.org) * All rights reserved. * + * Portions of this software were developed by Rui Paulo under sponsorship + * from the FreeBSD Foundation. + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -26,43 +30,524 @@ * $FreeBSD$ */ -#include "_libproc.h" +#include <sys/types.h> +#include <sys/user.h> + +#include <assert.h> +#include <err.h> #include <stdio.h> +#include <libgen.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <libutil.h> + +#include "_libproc.h" + +static void proc_rdl2prmap(rd_loadobj_t *, prmap_t *); + +static void +proc_rdl2prmap(rd_loadobj_t *rdl, prmap_t *map) +{ + map->pr_vaddr = rdl->rdl_saddr; + map->pr_size = rdl->rdl_eaddr - rdl->rdl_saddr; + map->pr_offset = rdl->rdl_offset; + map->pr_mflags = 0; + if (rdl->rdl_prot & RD_RDL_R) + map->pr_mflags |= MA_READ; + if (rdl->rdl_prot & RD_RDL_W) + map->pr_mflags |= MA_WRITE; + if (rdl->rdl_prot & RD_RDL_X) + map->pr_mflags |= MA_EXEC; + strlcpy(map->pr_mapname, rdl->rdl_path, + sizeof(map->pr_mapname)); +} char * proc_objname(struct proc_handle *p, uintptr_t addr, char *objname, size_t objnamesz) { -printf("%s(%d): Not implemented. p %p addr 0x%lx objname %p objnamesz %zd\n",__func__,__LINE__,p,(u_long) addr,objname,objnamesz); + size_t i; + rd_loadobj_t *rdl; + + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + if (addr >= rdl->rdl_saddr && addr <= rdl->rdl_eaddr) { + strlcpy(objname, rdl->rdl_path, objnamesz); + return (objname); + } + } return (NULL); } -const prmap_t * +prmap_t * +proc_obj2map(struct proc_handle *p, const char *objname) +{ + size_t i; + prmap_t *map; + rd_loadobj_t *rdl; + char path[MAXPATHLEN]; + + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + basename_r(rdl->rdl_path, path); + if (strcmp(path, objname) == 0) { + if ((map = malloc(sizeof(*map))) == NULL) + return (NULL); + proc_rdl2prmap(rdl, map); + return (map); + } + } + return (NULL); +} + +int +proc_iter_objs(struct proc_handle *p, proc_map_f *func, void *cd) +{ + size_t i; + rd_loadobj_t *rdl; + prmap_t map; + char path[MAXPATHLEN]; + + if (p->nobjs == 0) + return (-1); + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + proc_rdl2prmap(rdl, &map); + basename_r(rdl->rdl_path, path); + (*func)(cd, &map, path); + } + + return (0); +} + +prmap_t * proc_addr2map(struct proc_handle *p, uintptr_t addr) { -printf("%s(%d): Not implemented. p %p addr 0x%lx\n",__func__,__LINE__,p,(u_long) addr); + size_t i; + int cnt, lastvn = 0; + prmap_t *map; + rd_loadobj_t *rdl; + struct kinfo_vmentry *kves, *kve; + + /* + * If we don't have a cache of listed objects, we need to query + * it ourselves. + */ + if (p->nobjs == 0) { + if ((kves = kinfo_getvmmap(p->pid, &cnt)) == NULL) + return (NULL); + for (i = 0; i < (size_t)cnt; i++) { + kve = kves + i; + if (kve->kve_type == KVME_TYPE_VNODE) + lastvn = i; + if (addr >= kve->kve_start && addr <= kve->kve_end) { + if ((map = malloc(sizeof(*map))) == NULL) { + free(kves); + return (NULL); + } + map->pr_vaddr = kve->kve_start; + map->pr_size = kve->kve_end - kve->kve_start; + map->pr_offset = kve->kve_offset; + map->pr_mflags = 0; + if (kve->kve_protection & KVME_PROT_READ) + map->pr_mflags |= MA_READ; + if (kve->kve_protection & KVME_PROT_WRITE) + map->pr_mflags |= MA_WRITE; + if (kve->kve_protection & KVME_PROT_EXEC) + map->pr_mflags |= MA_EXEC; + if (kve->kve_flags & KVME_FLAG_COW) + map->pr_mflags |= MA_COW; + if (kve->kve_flags & KVME_FLAG_NEEDS_COPY) + map->pr_mflags |= MA_NEEDS_COPY; + if (kve->kve_flags & KVME_FLAG_NOCOREDUMP) + map->pr_mflags |= MA_NOCOREDUMP; + strlcpy(map->pr_mapname, kves[lastvn].kve_path, + sizeof(map->pr_mapname)); + free(kves); + return (map); + } + } + free(kves); + return (NULL); + } + + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + if (addr >= rdl->rdl_saddr && addr <= rdl->rdl_eaddr) { + if ((map = malloc(sizeof(*map))) == NULL) + return (NULL); + proc_rdl2prmap(rdl, map); + return (map); + } + } return (NULL); } int proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, - size_t namesz, GElf_Sym *sym) + size_t namesz, GElf_Sym *symcopy) { -printf("%s(%d): Not implemented. p %p addr 0x%lx name %p namesz %zd sym %p\n",__func__,__LINE__,p,(u_long) addr,name,namesz,sym); - return (0); + Elf *e; + Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; + Elf_Data *data; + GElf_Shdr shdr; + GElf_Sym sym; + GElf_Ehdr ehdr; + int fd, error = -1; + size_t i; + uint64_t rsym; + prmap_t *map; + char *s; + unsigned long symtabstridx = 0, dynsymstridx = 0; + + if ((map = proc_addr2map(p, addr)) == NULL) + return (-1); + if (!map->pr_mapname || (fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) { + warn("ERROR: open %s failed", map->pr_mapname); + goto err0; + } + if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + warn("ERROR: elf_begin() failed"); + goto err1; + } + if (gelf_getehdr(e, &ehdr) == NULL) { + warn("ERROR: gelf_getehdr() failed"); + goto err2; + } + /* + * Find the index of the STRTAB and SYMTAB sections to locate + * symbol names. + */ + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + gelf_getshdr(scn, &shdr); + switch (shdr.sh_type) { + case SHT_SYMTAB: + symtabscn = scn; + symtabstridx = shdr.sh_link; + break; + case SHT_DYNSYM: + dynsymscn = scn; + dynsymstridx = shdr.sh_link; + break; + default: + break; + } + } + /* + * Iterate over the Dynamic Symbols table to find the symbol. + * Then look up the string name in STRTAB (.dynstr) + */ + if ((data = elf_getdata(dynsymscn, NULL)) == NULL) { + DPRINTF("ERROR: elf_getdata() failed"); + goto err2; + } + i = 0; + while (gelf_getsym(data, i++, &sym) != NULL) { + /* + * Calculate the address mapped to the virtual memory + * by rtld. + */ + rsym = map->pr_vaddr + sym.st_value; + if (addr >= rsym && addr <= (rsym + sym.st_size)) { + s = elf_strptr(e, dynsymstridx, sym.st_name); + if (s) { + strlcpy(name, s, namesz); + memcpy(symcopy, &sym, sizeof(sym)); + /* + * DTrace expects the st_value to contain + * only the address relative to the start of + * the function. + */ + symcopy->st_value = rsym; + error = 0; + goto out; + } + } + } + /* + * Iterate over the Symbols Table to find the symbol. + * Then look up the string name in STRTAB (.dynstr) + */ + if (symtabscn == NULL) + goto err2; + if ((data = elf_getdata(symtabscn, NULL)) == NULL) { + DPRINTF("ERROR: elf_getdata() failed"); + goto err2; + } + i = 0; + while (gelf_getsym(data, i++, &sym) != NULL) { + /* + * Calculate the address mapped to the virtual memory + * by rtld. + */ + if (ehdr.e_type != ET_EXEC) + rsym = map->pr_vaddr + sym.st_value; + else + rsym = sym.st_value; + if (addr >= rsym && addr <= (rsym + sym.st_size)) { + s = elf_strptr(e, symtabstridx, sym.st_name); + if (s) { + strlcpy(name, s, namesz); + memcpy(symcopy, &sym, sizeof(sym)); + /* + * DTrace expects the st_value to contain + * only the address relative to the start of + * the function. + */ + symcopy->st_value = rsym; + error = 0; + goto out; + } + } + } +out: +err2: + elf_end(e); +err1: + close(fd); +err0: + free(map); + return (error); } -const prmap_t * +prmap_t * proc_name2map(struct proc_handle *p, const char *name) { -printf("%s(%d): Not implemented. p %p name %p\n",__func__,__LINE__,p,name); + size_t i; + int cnt; + prmap_t *map; + char tmppath[MAXPATHLEN]; + struct kinfo_vmentry *kves, *kve; + rd_loadobj_t *rdl; + + /* + * If we haven't iterated over the list of loaded objects, + * librtld_db isn't yet initialized and it's very likely + * that librtld_db called us. We need to do the heavy + * lifting here to find the symbol librtld_db is looking for. + */ + if (p->nobjs == 0) { + if ((kves = kinfo_getvmmap(proc_getpid(p), &cnt)) == NULL) + return (NULL); + for (i = 0; i < (size_t)cnt; i++) { + kve = kves + i; + basename_r(kve->kve_path, tmppath); + if (strcmp(tmppath, name) == 0) { + map = proc_addr2map(p, kve->kve_start); + free(kves); + return (map); + } + } + free(kves); + return (NULL); + } + if (name == NULL || strcmp(name, "a.out") == 0) { + map = proc_addr2map(p, p->rdobjs[0].rdl_saddr); + return (map); + } + for (i = 0; i < p->nobjs; i++) { + rdl = &p->rdobjs[i]; + basename_r(rdl->rdl_path, tmppath); + if (strcmp(tmppath, name) == 0) { + if ((map = malloc(sizeof(*map))) == NULL) + return (NULL); + proc_rdl2prmap(rdl, map); + return (map); + } + } + return (NULL); } int proc_name2sym(struct proc_handle *p, const char *object, const char *symbol, - GElf_Sym *sym) + GElf_Sym *symcopy) { -printf("%s(%d): Not implemented. p %p object %p symbol %p sym %p\n",__func__,__LINE__,p,object,symbol,sym); - return (0); + Elf *e; + Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; + Elf_Data *data; + GElf_Shdr shdr; + GElf_Sym sym; + GElf_Ehdr ehdr; + int fd, error = -1; + size_t i; + prmap_t *map; + char *s; + unsigned long symtabstridx = 0, dynsymstridx = 0; + + if ((map = proc_name2map(p, object)) == NULL) { + DPRINTF("ERROR: couldn't find object %s", object); + goto err0; + } + if ((fd = open(map->pr_mapname, O_RDONLY, 0)) < 0) { + DPRINTF("ERROR: open %s failed", map->pr_mapname); + goto err0; + } + if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + warn("ERROR: elf_begin() failed"); + goto err1; + } + if (gelf_getehdr(e, &ehdr) == NULL) { + warn("ERROR: gelf_getehdr() failed"); + goto err2; + } + /* + * Find the index of the STRTAB and SYMTAB sections to locate + * symbol names. + */ + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + gelf_getshdr(scn, &shdr); + switch (shdr.sh_type) { + case SHT_SYMTAB: + symtabscn = scn; + symtabstridx = shdr.sh_link; + break; + case SHT_DYNSYM: + dynsymscn = scn; + dynsymstridx = shdr.sh_link; + break; + default: + break; + } + } + /* + * Iterate over the Dynamic Symbols table to find the symbol. + * Then look up the string name in STRTAB (.dynstr) + */ + if ((data = elf_getdata(dynsymscn, NULL)) == NULL) { + DPRINTF("ERROR: elf_getdata() failed"); + goto err2; + } + i = 0; + while (gelf_getsym(data, i++, &sym) != NULL) { + s = elf_strptr(e, dynsymstridx, sym.st_name); + if (s && strcmp(s, symbol) == 0) { + memcpy(symcopy, &sym, sizeof(sym)); + symcopy->st_value = map->pr_vaddr + sym.st_value; + error = 0; + goto out; + } + } + /* + * Iterate over the Symbols Table to find the symbol. + * Then look up the string name in STRTAB (.dynstr) + */ + if (symtabscn == NULL) + goto err2; + if ((data = elf_getdata(symtabscn, NULL)) == NULL) { + DPRINTF("ERROR: elf_getdata() failed"); + goto err2; + } + i = 0; + while (gelf_getsym(data, i++, &sym) != NULL) { + s = elf_strptr(e, symtabstridx, sym.st_name); + if (s && strcmp(s, symbol) == 0) { + memcpy(symcopy, &sym, sizeof(sym)); + error = 0; + goto out; + } + } +out: +err2: + elf_end(e); +err1: + close(fd); +err0: + free(map); + + return (error); +} + + +int +proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, + int mask, proc_sym_f *func, void *cd) +{ + Elf *e; + int i, fd; + prmap_t *map; + Elf_Scn *scn, *foundscn = NULL; + Elf_Data *data; + GElf_Shdr shdr; + GElf_Sym sym; + unsigned long stridx = -1; + char *s; + int error = -1; + + if ((map = proc_name2map(p, object)) == NULL) + return (-1); + if ((fd = open(map->pr_mapname, O_RDONLY)) < 0) { + warn("ERROR: open %s failed", map->pr_mapname); + goto err0; + } + if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { + warn("ERROR: elf_begin() failed"); + goto err1; + } + /* + * Find the section we are looking for. + */ + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + gelf_getshdr(scn, &shdr); + if (which == PR_SYMTAB && + shdr.sh_type == SHT_SYMTAB) { + foundscn = scn; + break; + } else if (which == PR_DYNSYM && + shdr.sh_type == SHT_DYNSYM) { + foundscn = scn; + break; + } + } + if (!foundscn) + return (-1); + stridx = shdr.sh_link; + if ((data = elf_getdata(foundscn, NULL)) == NULL) { + DPRINTF("ERROR: elf_getdata() failed"); + goto err2; + } + i = 0; + while (gelf_getsym(data, i++, &sym) != NULL) { + if (GELF_ST_BIND(sym.st_info) == STB_LOCAL && + (mask & BIND_LOCAL) == 0) + continue; + if (GELF_ST_BIND(sym.st_info) == STB_GLOBAL && + (mask & BIND_GLOBAL) == 0) + continue; + if (GELF_ST_BIND(sym.st_info) == STB_WEAK && + (mask & BIND_WEAK) == 0) + continue; + if (GELF_ST_TYPE(sym.st_info) == STT_NOTYPE && + (mask & TYPE_NOTYPE) == 0) + continue; + if (GELF_ST_TYPE(sym.st_info) == STT_OBJECT && + (mask & TYPE_OBJECT) == 0) + continue; + if (GELF_ST_TYPE(sym.st_info) == STT_FUNC && + (mask & TYPE_FUNC) == 0) + continue; + if (GELF_ST_TYPE(sym.st_info) == STT_SECTION && + (mask & TYPE_SECTION) == 0) + continue; + if (GELF_ST_TYPE(sym.st_info) == STT_FILE && + (mask & TYPE_FILE) == 0) + continue; + s = elf_strptr(e, stridx, sym.st_name); + sym.st_value += map->pr_vaddr; + (*func)(cd, &sym, s); + } + error = 0; +err2: + elf_end(e); +err1: + close(fd); +err0: + free(map); + return (error); } |