From owner-svn-src-all@freebsd.org Tue Dec 6 04:21:36 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id A6087C698A7; Tue, 6 Dec 2016 04:21:36 +0000 (UTC) (envelope-from markj@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id 751FA613; Tue, 6 Dec 2016 04:21:36 +0000 (UTC) (envelope-from markj@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id uB64LZsO064619; Tue, 6 Dec 2016 04:21:35 GMT (envelope-from markj@FreeBSD.org) Received: (from markj@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id uB64LZaf064616; Tue, 6 Dec 2016 04:21:35 GMT (envelope-from markj@FreeBSD.org) Message-Id: <201612060421.uB64LZaf064616@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: markj set sender to markj@FreeBSD.org using -f From: Mark Johnston Date: Tue, 6 Dec 2016 04:21:35 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r309596 - head/lib/libproc X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 06 Dec 2016 04:21:36 -0000 Author: markj Date: Tue Dec 6 04:21:35 2016 New Revision: 309596 URL: https://svnweb.freebsd.org/changeset/base/309596 Log: libproc: Cache symbol tables for mapped objects upon access. Extend the file handle cache entries to include symbol tables as well. An index is used to implement binary search by symbol value. Lookups by name are comparatively rare and are thus still implemented with a linear search, but support for a binary search by name would be straightforward to add if needed. Modified: head/lib/libproc/_libproc.h head/lib/libproc/proc_create.c head/lib/libproc/proc_sym.c Modified: head/lib/libproc/_libproc.h ============================================================================== --- head/lib/libproc/_libproc.h Tue Dec 6 04:20:32 2016 (r309595) +++ head/lib/libproc/_libproc.h Tue Dec 6 04:21:35 2016 (r309596) @@ -39,10 +39,22 @@ struct procstat; +struct symtab { + Elf_Data *data; + u_int nsyms; + u_int *index; + u_long stridx; +}; + struct file_info { Elf *elf; int fd; u_int refs; + GElf_Ehdr ehdr; + + /* Symbol tables, sorted by value. */ + struct symtab dynsymtab; + struct symtab symtab; }; struct map_info { Modified: head/lib/libproc/proc_create.c ============================================================================== --- head/lib/libproc/proc_create.c Tue Dec 6 04:20:32 2016 (r309595) +++ head/lib/libproc/proc_create.c Tue Dec 6 04:21:35 2016 (r309596) @@ -239,6 +239,10 @@ proc_free(struct proc_handle *phdl) if (file->elf != NULL) { (void)elf_end(file->elf); (void)close(file->fd); + if (file->symtab.nsyms > 0) + free(file->symtab.index); + if (file->dynsymtab.nsyms > 0) + free(file->dynsymtab.index); } free(file); } Modified: head/lib/libproc/proc_sym.c ============================================================================== --- head/lib/libproc/proc_sym.c Tue Dec 6 04:20:32 2016 (r309595) +++ head/lib/libproc/proc_sym.c Tue Dec 6 04:21:35 2016 (r309596) @@ -101,6 +101,74 @@ fail: } static int +symvalcomp(void *thunk, const void *a1, const void *a2) +{ + struct symtab *symtab; + GElf_Sym sym1, sym2; + u_int i1, i2; + int ret; + + i1 = *(const u_int *)a1; + i2 = *(const u_int *)a2; + symtab = thunk; + + (void)gelf_getsym(symtab->data, i1, &sym1); + (void)gelf_getsym(symtab->data, i2, &sym2); + if (sym1.st_value < sym2.st_value) + ret = -1; + else if (sym1.st_value == sym2.st_value) + ret = 0; + else + ret = 1; + return (ret); +} + +static int +load_symtab(Elf *e, struct symtab *symtab, u_long sh_type) +{ + GElf_Ehdr ehdr; + GElf_Shdr shdr; + Elf_Scn *scn; + u_int nsyms; + + if (gelf_getehdr(e, &ehdr) == NULL) + return (-1); + + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + (void)gelf_getshdr(scn, &shdr); + if (shdr.sh_type == sh_type) + break; + } + if (scn == NULL) + return (-1); + + if ((symtab->data = elf_getdata(scn, NULL)) == NULL) + return (-1); + + nsyms = shdr.sh_size / shdr.sh_entsize; + + symtab->index = calloc(nsyms, sizeof(u_int)); + if (symtab->index == NULL) + return (-1); + for (u_int i = 0; i < nsyms; i++) + symtab->index[i] = i; + qsort_r(symtab->index, nsyms, sizeof(u_int), symtab, symvalcomp); + symtab->nsyms = nsyms; + symtab->stridx = shdr.sh_link; + return (0); +} + +static void +load_symtabs(struct file_info *file) +{ + + file->symtab.nsyms = file->dynsymtab.nsyms = 0; + (void)load_symtab(file->elf, &file->symtab, SHT_SYMTAB); + (void)load_symtab(file->elf, &file->dynsymtab, SHT_DYNSYM); +} + +static int open_debug_file(char *path, const char *debugfile, uint32_t crc) { size_t n; @@ -159,6 +227,10 @@ open_object(struct map_info *mapping) DPRINTFX("ERROR: elf_begin() failed: %s", elf_errmsg(-1)); goto err; } + if (gelf_getehdr(e, &file->ehdr) != &file->ehdr) { + DPRINTFX("ERROR: elf_getehdr() failed: %s", elf_errmsg(-1)); + goto err; + } scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { @@ -235,6 +307,7 @@ internal: /* We didn't find a debug file, just return the object's descriptor. */ file->elf = e; file->fd = fd; + load_symtabs(file); return (0); external: @@ -247,6 +320,7 @@ external: (void)close(fd); file->elf = e2; file->fd = fd2; + load_symtabs(file); return (0); err: @@ -333,55 +407,49 @@ proc_addr2map(struct proc_handle *p, uin } /* - * Look up the symbol at addr, returning a copy of the symbol and its name. + * Look up the symbol at addr using a binary search, returning a copy of the + * symbol and its name. */ static int -lookup_addr(Elf *e, Elf_Scn *scn, u_long stridx, uintptr_t off, uintptr_t addr, - const char **name, GElf_Sym *symcopy) +lookup_symbol_by_addr(Elf *elf, struct symtab *symtab, uintptr_t addr, + const char **namep, GElf_Sym *sym) { - GElf_Sym sym; Elf_Data *data; const char *s; - uint64_t rsym; - int i; + int min, max, mid; - if ((data = elf_getdata(scn, NULL)) == NULL) { - DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); - return (1); - } - for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { - rsym = off + sym.st_value; - if (addr >= rsym && addr < rsym + sym.st_size) { - s = elf_strptr(e, stridx, sym.st_name); - if (s != NULL) { - *name = s; - memcpy(symcopy, &sym, sizeof(*symcopy)); - /* - * DTrace expects the st_value to contain - * only the address relative to the start of - * the function. - */ - symcopy->st_value = rsym; - return (0); - } + data = symtab->data; + min = 0; + max = symtab->nsyms - 1; + + while (min <= max) { + mid = (max + min) / 2; + (void)gelf_getsym(data, symtab->index[mid], sym); + if (addr >= sym->st_value && + addr < sym->st_value + sym->st_size) { + s = elf_strptr(elf, symtab->stridx, sym->st_name); + if (s != NULL && namep != NULL) + *namep = s; + return (0); } + + if (addr < sym->st_value) + max = mid - 1; + else + min = mid + 1; } - return (1); + return (ENOENT); } int proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, size_t namesz, GElf_Sym *symcopy) { - GElf_Ehdr ehdr; - GElf_Shdr shdr; - Elf *e; - Elf_Scn *scn, *dynsymscn, *symtabscn; + struct file_info *file; struct map_info *mapping; const char *s; uintptr_t off; - u_long symtabstridx, dynsymstridx; - int error = -1; + int error; if ((mapping = _proc_addr2map(p, addr)) == NULL) { DPRINTFX("ERROR: proc_addr2map failed to resolve 0x%jx", addr); @@ -392,49 +460,22 @@ proc_addr2sym(struct proc_handle *p, uin mapping->map.pr_mapname); return (-1); } - e = mapping->file->elf; - if (gelf_getehdr(e, &ehdr) == NULL) { - DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); - goto err; - } - /* - * Find the index of the STRTAB and SYMTAB sections to locate - * symbol names. - */ - symtabstridx = dynsymstridx = 0; - scn = dynsymscn = symtabscn = 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; - } + file = mapping->file; + off = file->ehdr.e_type == ET_DYN ? mapping->map.pr_vaddr : 0; + if (addr < off) + return (ENOENT); + addr -= off; + + error = lookup_symbol_by_addr(file->elf, &file->dynsymtab, addr, &s, + symcopy); + if (error == ENOENT) + error = lookup_symbol_by_addr(file->elf, &file->symtab, addr, + &s, symcopy); + if (error == 0) { + symcopy->st_value += off; + demangle(s, name, namesz); } - - off = ehdr.e_type == ET_EXEC ? 0 : mapping->map.pr_vaddr; - - /* - * First look up the symbol in the dynsymtab, and fall back to the - * symtab if the lookup fails. - */ - error = lookup_addr(e, dynsymscn, dynsymstridx, off, addr, &s, symcopy); - if (error == 0) - goto out; - - error = lookup_addr(e, symtabscn, symtabstridx, off, addr, &s, symcopy); - if (error != 0) - goto err; - -out: - demangle(s, name, namesz); -err: return (error); } @@ -480,7 +521,7 @@ proc_name2map(struct proc_handle *p, con * Look up the symbol with the given name and return a copy of it. */ static int -lookup_name(Elf *e, Elf_Scn *scn, u_long stridx, const char *symbol, +lookup_symbol_by_name(Elf *elf, struct symtab *symtab, const char *symbol, GElf_Sym *symcopy, prsyminfo_t *si) { GElf_Sym sym; @@ -488,12 +529,11 @@ lookup_name(Elf *e, Elf_Scn *scn, u_long char *s; int i; - if ((data = elf_getdata(scn, NULL)) == NULL) { - DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); - return (1); - } + if (symtab->nsyms == 0) + return (ENOENT); + data = symtab->data; for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { - s = elf_strptr(e, stridx, sym.st_name); + s = elf_strptr(elf, symtab->stridx, sym.st_name); if (s != NULL && strcmp(s, symbol) == 0) { memcpy(symcopy, &sym, sizeof(*symcopy)); if (si != NULL) @@ -501,21 +541,17 @@ lookup_name(Elf *e, Elf_Scn *scn, u_long return (0); } } - return (1); + return (ENOENT); } int proc_name2sym(struct proc_handle *p, const char *object, const char *symbol, GElf_Sym *symcopy, prsyminfo_t *si) { - GElf_Ehdr ehdr; - GElf_Shdr shdr; - Elf *e; - Elf_Scn *scn, *dynsymscn, *symtabscn; + struct file_info *file; struct map_info *mapping; uintptr_t off; - u_long symtabstridx, dynsymstridx; - int error = -1; + int error; if ((mapping = _proc_name2map(p, object)) == NULL) { DPRINTFX("ERROR: proc_name2map failed to resolve %s", object); @@ -526,49 +562,17 @@ proc_name2sym(struct proc_handle *p, con mapping->map.pr_mapname); return (-1); } - e = mapping->file->elf; - if (gelf_getehdr(e, &ehdr) == NULL) { - DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); - goto err; - } - /* - * Find the index of the STRTAB and SYMTAB sections to locate - * symbol names. - */ - symtabstridx = dynsymstridx = 0; - scn = dynsymscn = symtabscn = 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; - } - } - - /* - * First look up the symbol in the dynsymtab, and fall back to the - * symtab if the lookup fails. - */ - error = lookup_name(e, dynsymscn, dynsymstridx, symbol, symcopy, si); - if (error == 0) - goto out; + file = mapping->file; + off = file->ehdr.e_type == ET_DYN ? mapping->map.pr_vaddr : 0; - error = lookup_name(e, symtabscn, symtabstridx, symbol, symcopy, si); + error = lookup_symbol_by_name(file->elf, &file->dynsymtab, symbol, + symcopy, si); + if (error == ENOENT) + error = lookup_symbol_by_name(file->elf, &file->symtab, symbol, + symcopy, si); if (error == 0) - goto out; - -out: - off = ehdr.e_type == ET_EXEC ? 0 : mapping->map.pr_vaddr; - symcopy->st_value += off; - -err: + symcopy->st_value += off; return (error); } @@ -596,16 +600,12 @@ int proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, int mask, proc_sym_f *func, void *cd) { - GElf_Ehdr ehdr; - GElf_Shdr shdr; GElf_Sym sym; - Elf *e; - Elf_Scn *scn, *foundscn; - Elf_Data *data; + struct file_info *file; struct map_info *mapping; - char *s; - unsigned long stridx = -1; - int error = -1, i; + struct symtab *symtab; + const char *s; + int error, i; if ((mapping = _proc_name2map(p, object)) == NULL) { DPRINTFX("ERROR: proc_name2map failed to resolve %s", object); @@ -616,35 +616,14 @@ proc_iter_symbyaddr(struct proc_handle * mapping->map.pr_mapname); return (-1); } - e = mapping->file->elf; - if (gelf_getehdr(e, &ehdr) == NULL) { - DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); - goto err; - } - /* - * Find the section we are looking for. - */ - foundscn = 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) + + file = mapping->file; + symtab = which == PR_SYMTAB ? &file->symtab : &file->dynsymtab; + if (symtab->nsyms == 0) return (-1); - stridx = shdr.sh_link; - if ((data = elf_getdata(foundscn, NULL)) == NULL) { - DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); - goto err; - } - for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { + + error = 0; + for (i = 0; gelf_getsym(symtab->data, i, &sym) != NULL; i++) { if (GELF_ST_BIND(sym.st_info) == STB_LOCAL && (mask & BIND_LOCAL) == 0) continue; @@ -669,13 +648,11 @@ proc_iter_symbyaddr(struct proc_handle * if (GELF_ST_TYPE(sym.st_info) == STT_FILE && (mask & TYPE_FILE) == 0) continue; - s = elf_strptr(e, stridx, sym.st_name); - if (ehdr.e_type != ET_EXEC) + s = elf_strptr(file->elf, symtab->stridx, sym.st_name); + if (file->ehdr.e_type == ET_DYN) sym.st_value += mapping->map.pr_vaddr; if ((error = (*func)(cd, &sym, s)) != 0) - goto err; + break; } - error = 0; -err: return (error); }