Factor out some of the duplicated code in the symbol lookup functions, in

preparation for adding userland CTF support to DTrace.

MFC after:	1 month
Sponsored by:	EMC / Isilon Storage Division
This commit is contained in:
Mark Johnston 2014-09-25 19:08:06 +00:00
parent 43776a00e2
commit 540cc663b1
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=272125

View file

@ -26,20 +26,20 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
*
* $FreeBSD$
*/ */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/types.h> #include <sys/types.h>
#include <sys/user.h> #include <sys/user.h>
#include <assert.h> #include <assert.h>
#include <err.h> #include <err.h>
#include <stdio.h>
#include <libgen.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h> #include <fcntl.h>
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <libutil.h> #include <libutil.h>
@ -228,22 +228,56 @@ proc_addr2map(struct proc_handle *p, uintptr_t addr)
return (NULL); return (NULL);
} }
/*
* Look up the symbol at addr, 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)
{
GElf_Sym sym;
Elf_Data *data;
const char *s;
uint64_t rsym;
int i;
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);
}
}
}
return (1);
}
int int
proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name,
size_t namesz, GElf_Sym *symcopy) size_t namesz, GElf_Sym *symcopy)
{ {
GElf_Ehdr ehdr;
GElf_Shdr shdr;
Elf *e; Elf *e;
Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; 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; prmap_t *map;
char *s; const char *s;
unsigned long symtabstridx = 0, dynsymstridx = 0; uintptr_t off;
u_long symtabstridx = 0, dynsymstridx = 0;
int fd, error = -1;
if ((map = proc_addr2map(p, addr)) == NULL) if ((map = proc_addr2map(p, addr)) == NULL)
return (-1); return (-1);
@ -259,6 +293,7 @@ proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name,
DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1));
goto err2; goto err2;
} }
/* /*
* Find the index of the STRTAB and SYMTAB sections to locate * Find the index of the STRTAB and SYMTAB sections to locate
* symbol names. * symbol names.
@ -275,80 +310,25 @@ proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name,
dynsymscn = scn; dynsymscn = scn;
dynsymstridx = shdr.sh_link; dynsymstridx = shdr.sh_link;
break; break;
default:
break;
} }
} }
off = ehdr.e_type == ET_EXEC ? 0 : map->pr_vaddr;
/* /*
* Iterate over the Dynamic Symbols table to find the symbol. * First look up the symbol in the dynsymtab, and fall back to the
* Then look up the string name in STRTAB (.dynstr) * symtab if the lookup fails.
*/ */
if ((data = elf_getdata(dynsymscn, NULL)) == NULL) { error = lookup_addr(e, dynsymscn, dynsymstridx, off, addr, &s, symcopy);
DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); if (error == 0)
goto symtab; goto out;
}
i = 0; error = lookup_addr(e, symtabscn, symtabstridx, off, addr, &s, symcopy);
while (gelf_getsym(data, i++, &sym) != NULL) { if (error == 0)
/* goto out;
* 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, dynsymstridx, sym.st_name);
if (s) {
demangle(s, name, 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;
}
}
}
symtab:
/*
* Iterate over the Symbols Table to find the symbol.
* Then look up the string name in STRTAB (.dynstr)
*/
if ((data = elf_getdata(symtabscn, NULL)) == NULL) {
DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
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) {
demangle(s, name, 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: out:
demangle(s, name, namesz);
err2: err2:
elf_end(e); elf_end(e);
err1: err1:
@ -363,7 +343,7 @@ proc_name2map(struct proc_handle *p, const char *name)
{ {
size_t i; size_t i;
int cnt; int cnt;
prmap_t *map; prmap_t *map = NULL;
char tmppath[MAXPATHLEN]; char tmppath[MAXPATHLEN];
struct kinfo_vmentry *kves, *kve; struct kinfo_vmentry *kves, *kve;
rd_loadobj_t *rdl; rd_loadobj_t *rdl;
@ -382,30 +362,52 @@ proc_name2map(struct proc_handle *p, const char *name)
basename_r(kve->kve_path, tmppath); basename_r(kve->kve_path, tmppath);
if (strcmp(tmppath, name) == 0) { if (strcmp(tmppath, name) == 0) {
map = proc_addr2map(p, kve->kve_start); map = proc_addr2map(p, kve->kve_start);
free(kves); break;
return (map);
} }
} }
free(kves); free(kves);
return (NULL); } else
} for (i = 0; i < p->nobjs; i++) {
if ((name == NULL || strcmp(name, "a.out") == 0) && rdl = &p->rdobjs[i];
p->rdexec != NULL) { basename_r(rdl->rdl_path, tmppath);
if (strcmp(tmppath, name) == 0) {
if ((map = malloc(sizeof(*map))) == NULL)
return (NULL);
proc_rdl2prmap(rdl, map);
break;
}
}
if (map == NULL && strcmp(name, "a.out") == 0 && p->rdexec != NULL)
map = proc_addr2map(p, p->rdexec->rdl_saddr); map = proc_addr2map(p, p->rdexec->rdl_saddr);
return (map);
return (map);
}
/*
* 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,
GElf_Sym *symcopy)
{
GElf_Sym sym;
Elf_Data *data;
char *s;
int i;
if ((data = elf_getdata(scn, NULL)) == NULL) {
DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
return (1);
} }
for (i = 0; i < p->nobjs; i++) { for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) {
rdl = &p->rdobjs[i]; s = elf_strptr(e, stridx, sym.st_name);
basename_r(rdl->rdl_path, tmppath); if (s != NULL && strcmp(s, symbol) == 0) {
if (strcmp(tmppath, name) == 0) { memcpy(symcopy, &sym, sizeof(*symcopy));
if ((map = malloc(sizeof(*map))) == NULL) return (0);
return (NULL);
proc_rdl2prmap(rdl, map);
return (map);
} }
} }
return (1);
return (NULL);
} }
int int
@ -414,15 +416,12 @@ proc_name2sym(struct proc_handle *p, const char *object, const char *symbol,
{ {
Elf *e; Elf *e;
Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL; Elf_Scn *scn, *dynsymscn = NULL, *symtabscn = NULL;
Elf_Data *data;
GElf_Shdr shdr; GElf_Shdr shdr;
GElf_Sym sym;
GElf_Ehdr ehdr; GElf_Ehdr ehdr;
int fd, error = -1;
size_t i;
prmap_t *map; prmap_t *map;
char *s; uintptr_t off;
unsigned long symtabstridx = 0, dynsymstridx = 0; u_long symtabstridx = 0, dynsymstridx = 0;
int fd, error = -1;
if ((map = proc_name2map(p, object)) == NULL) { if ((map = proc_name2map(p, object)) == NULL) {
DPRINTFX("ERROR: couldn't find object %s", object); DPRINTFX("ERROR: couldn't find object %s", object);
@ -456,46 +455,25 @@ proc_name2sym(struct proc_handle *p, const char *object, const char *symbol,
dynsymscn = scn; dynsymscn = scn;
dynsymstridx = shdr.sh_link; dynsymstridx = shdr.sh_link;
break; break;
default:
break;
} }
} }
/* /*
* Iterate over the Dynamic Symbols table to find the symbol. * First look up the symbol in the dynsymtab, and fall back to the
* Then look up the string name in STRTAB (.dynstr) * symtab if the lookup fails.
*/ */
if ((data = elf_getdata(dynsymscn, NULL))) { error = lookup_name(e, dynsymscn, dynsymstridx, symbol, symcopy);
i = 0; if (error == 0)
while (gelf_getsym(data, i++, &sym) != NULL) { goto out;
s = elf_strptr(e, dynsymstridx, sym.st_name);
if (s && strcmp(s, symbol) == 0) { error = lookup_name(e, symtabscn, symtabstridx, symbol, symcopy);
memcpy(symcopy, &sym, sizeof(sym)); if (error == 0)
if (ehdr.e_type != ET_EXEC) goto out;
symcopy->st_value += map->pr_vaddr;
error = 0;
goto out;
}
}
}
/*
* Iterate over the Symbols Table to find the symbol.
* Then look up the string name in STRTAB (.dynstr)
*/
if ((data = elf_getdata(symtabscn, NULL))) {
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));
if (ehdr.e_type != ET_EXEC)
symcopy->st_value += map->pr_vaddr;
error = 0;
goto out;
}
}
}
out: out:
DPRINTFX("found addr 0x%lx for %s", symcopy->st_value, symbol); off = ehdr.e_type == ET_EXEC ? 0 : map->pr_vaddr;
symcopy->st_value += off;
err2: err2:
elf_end(e); elf_end(e);
err1: err1:
@ -506,7 +484,6 @@ proc_name2sym(struct proc_handle *p, const char *object, const char *symbol,
return (error); return (error);
} }
int int
proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which,
int mask, proc_sym_f *func, void *cd) int mask, proc_sym_f *func, void *cd)
@ -543,7 +520,7 @@ proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which,
scn = NULL; scn = NULL;
while ((scn = elf_nextscn(e, scn)) != NULL) { while ((scn = elf_nextscn(e, scn)) != NULL) {
gelf_getshdr(scn, &shdr); gelf_getshdr(scn, &shdr);
if (which == PR_SYMTAB && if (which == PR_SYMTAB &&
shdr.sh_type == SHT_SYMTAB) { shdr.sh_type == SHT_SYMTAB) {
foundscn = scn; foundscn = scn;
break; break;
@ -560,8 +537,7 @@ proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which,
DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1));
goto err2; goto err2;
} }
i = 0; for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) {
while (gelf_getsym(data, i++, &sym) != NULL) {
if (GELF_ST_BIND(sym.st_info) == STB_LOCAL && if (GELF_ST_BIND(sym.st_info) == STB_LOCAL &&
(mask & BIND_LOCAL) == 0) (mask & BIND_LOCAL) == 0)
continue; continue;