diff options
| author | Simon J. Gerraty <sjg@FreeBSD.org> | 2014-11-19 01:07:58 +0000 |
|---|---|---|
| committer | Simon J. Gerraty <sjg@FreeBSD.org> | 2014-11-19 01:07:58 +0000 |
| commit | 9268022b74279434ed6300244e3f977e56a8ceb5 (patch) | |
| tree | 377ac0ac449528621eb192cd245adadb5fd53668 /lib/libproc | |
| parent | 29c34e9d2781cf25403647fb5af7d7ddb23be7e1 (diff) | |
| parent | 8c3d6a4ab2a4a95d864d9a32d0157d7de90498a4 (diff) | |
Notes
Diffstat (limited to 'lib/libproc')
| -rw-r--r-- | lib/libproc/Makefile | 17 | ||||
| -rw-r--r-- | lib/libproc/libproc.h | 13 | ||||
| -rw-r--r-- | lib/libproc/proc_sym.c | 319 | ||||
| -rw-r--r-- | lib/libproc/test/Makefile | 5 | ||||
| -rw-r--r-- | lib/libproc/test/t1-bkpt/Makefile | 12 | ||||
| -rw-r--r-- | lib/libproc/test/t1-bkpt/t1-bkpt.c | 71 | ||||
| -rw-r--r-- | lib/libproc/test/t2-name2map/Makefile | 12 | ||||
| -rw-r--r-- | lib/libproc/test/t3-name2sym/Makefile | 12 | ||||
| -rw-r--r-- | lib/libproc/test/t3-name2sym/t3-name2sym.c | 51 | ||||
| -rw-r--r-- | lib/libproc/tests/Makefile | 21 | ||||
| -rw-r--r-- | lib/libproc/tests/proc_test.c | 349 | ||||
| -rw-r--r-- | lib/libproc/tests/target_prog.c (renamed from lib/libproc/test/t2-name2map/t2-name2map.c) | 89 |
12 files changed, 608 insertions, 363 deletions
diff --git a/lib/libproc/Makefile b/lib/libproc/Makefile index 5e5babffee70..bfb9492d703a 100644 --- a/lib/libproc/Makefile +++ b/lib/libproc/Makefile @@ -27,8 +27,23 @@ LDADD+= -lsupc++ DPADD+= ${LIBSTDCPLUSPLUS} .endif -SHLIB_MAJOR= 2 +.if ${MK_CDDL} != "no" +LDADD+= -lctf +DPADD+= ${LIBCTF} +IGNORE_PRAGMA= YES +CFLAGS+= -I${.CURDIR}/../../cddl/contrib/opensolaris/lib/libctf/common \ + -I${.CURDIR}/../../sys/cddl/contrib/opensolaris/uts/common \ + -I${.CURDIR}/../../sys/cddl/compat/opensolaris +.else +CFLAGS+= -DNO_CTF +.endif + +SHLIB_MAJOR= 3 MAN= +.if ${MK_TESTS} != "no" +SUBDIR+= tests +.endif + .include <bsd.lib.mk> diff --git a/lib/libproc/libproc.h b/lib/libproc/libproc.h index be77e4b59214..5d81c2bf4505 100644 --- a/lib/libproc/libproc.h +++ b/lib/libproc/libproc.h @@ -37,6 +37,7 @@ #include <rtld_db.h> #include <limits.h> +struct ctf_file; struct proc_handle; typedef void (*proc_child_func)(void *); @@ -67,6 +68,11 @@ typedef struct prmap { #define MA_NOCOREDUMP 0x20 } prmap_t; +typedef struct prsyminfo { + u_int prs_lmid; /* Map id. */ + u_int prs_id; /* Symbol id. */ +} prsyminfo_t; + typedef int proc_map_f(void *, const prmap_t *, const char *); typedef int proc_sym_f(void *, const GElf_Sym *, const char *); @@ -125,7 +131,9 @@ int proc_create(const char *, char * const *, proc_child_func *, void *, struct proc_handle **); int proc_detach(struct proc_handle *, int); int proc_getflags(struct proc_handle *); -int proc_name2sym(struct proc_handle *, const char *, const char *, GElf_Sym *); +int proc_name2sym(struct proc_handle *, const char *, const char *, + GElf_Sym *, prsyminfo_t *); +struct ctf_file *proc_name2ctf(struct proc_handle *, const char *); int proc_setflags(struct proc_handle *, int); int proc_state(struct proc_handle *); pid_t proc_getpid(struct proc_handle *); @@ -133,8 +141,7 @@ int proc_wstatus(struct proc_handle *); int proc_getwstat(struct proc_handle *); char * proc_signame(int, char *, size_t); int proc_read(struct proc_handle *, void *, size_t, size_t); -const lwpstatus_t * - proc_getlwpstatus(struct proc_handle *); +const lwpstatus_t *proc_getlwpstatus(struct proc_handle *); void proc_free(struct proc_handle *); rd_agent_t *proc_rdagent(struct proc_handle *); void proc_updatesyms(struct proc_handle *); diff --git a/lib/libproc/proc_sym.c b/lib/libproc/proc_sym.c index 4d600436c676..766ff73446e2 100644 --- a/lib/libproc/proc_sym.c +++ b/lib/libproc/proc_sym.c @@ -26,26 +26,37 @@ * 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 * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + #include <sys/types.h> +#ifndef NO_CTF +#include <sys/ctf.h> +#include <sys/ctf_api.h> +#endif #include <sys/user.h> #include <assert.h> #include <err.h> -#include <stdio.h> +#include <fcntl.h> #include <libgen.h> -#include <string.h> +#include <stdio.h> #include <stdlib.h> -#include <fcntl.h> #include <string.h> #include <unistd.h> +#ifndef NO_CTF +#include <libctf.h> +#endif #include <libutil.h> #include "_libproc.h" +#ifdef NO_CTF +typedef struct ctf_file ctf_file_t; +#endif + #ifndef NO_CXA_DEMANGLE extern char *__cxa_demangle(const char *, char *, size_t *, int *); #endif /* NO_CXA_DEMANGLE */ @@ -57,21 +68,15 @@ demangle(const char *symbol, char *buf, size_t len) { #ifndef NO_CXA_DEMANGLE char *dembuf; - size_t demlen; if (symbol[0] == '_' && symbol[1] == 'Z' && symbol[2]) { - dembuf = malloc(len); - if (!dembuf) - goto fail; - demlen = len; - dembuf = __cxa_demangle(symbol, dembuf, &demlen, NULL); + dembuf = __cxa_demangle(symbol, NULL, NULL, NULL); if (!dembuf) goto fail; strlcpy(buf, dembuf, len); free(dembuf); + return; } - - return; fail: #endif /* NO_CXA_DEMANGLE */ strlcpy(buf, symbol, len); @@ -127,10 +132,12 @@ proc_obj2map(struct proc_handle *p, const char *objname) break; } } - if (rdl == NULL && strcmp(objname, "a.out") == 0 && p->rdexec != NULL) - rdl = p->rdexec; - else - return (NULL); + if (rdl == NULL) { + if (strcmp(objname, "a.out") == 0 && p->rdexec != NULL) + rdl = p->rdexec; + else + return (NULL); + } if ((map = malloc(sizeof(*map))) == NULL) return (NULL); @@ -232,22 +239,56 @@ proc_addr2map(struct proc_handle *p, uintptr_t addr) 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 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 = 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; + const char *s; + uintptr_t off; + u_long symtabstridx = 0, dynsymstridx = 0; + int fd, error = -1; if ((map = proc_addr2map(p, addr)) == NULL) return (-1); @@ -263,6 +304,7 @@ proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, DPRINTFX("ERROR: gelf_getehdr() failed: %s", elf_errmsg(-1)); goto err2; } + /* * Find the index of the STRTAB and SYMTAB sections to locate * symbol names. @@ -279,80 +321,25 @@ proc_addr2sym(struct proc_handle *p, uintptr_t addr, char *name, 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) { - DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); - goto symtab; - } - 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, 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: + + off = ehdr.e_type == ET_EXEC ? 0 : map->pr_vaddr; + /* - * Iterate over the Symbols Table to find the symbol. - * Then look up the string name in STRTAB (.dynstr) + * First look up the symbol in the dynsymtab, and fall back to the + * symtab if the lookup fails. */ - 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; - } - } - } + 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 out; + out: + demangle(s, name, namesz); err2: elf_end(e); err1: @@ -367,7 +354,7 @@ proc_name2map(struct proc_handle *p, const char *name) { size_t i; int cnt; - prmap_t *map; + prmap_t *map = NULL; char tmppath[MAXPATHLEN]; struct kinfo_vmentry *kves, *kve; rd_loadobj_t *rdl; @@ -386,47 +373,68 @@ proc_name2map(struct proc_handle *p, const char *name) basename_r(kve->kve_path, tmppath); if (strcmp(tmppath, name) == 0) { map = proc_addr2map(p, kve->kve_start); - free(kves); - return (map); + break; } } free(kves); - return (NULL); - } - if ((name == NULL || strcmp(name, "a.out") == 0) && - p->rdexec != NULL) { + } else + 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); + break; + } + } + + if (map == NULL && strcmp(name, "a.out") == 0 && p->rdexec != NULL) 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, prsyminfo_t *si) +{ + 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++) { - 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); + for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { + s = elf_strptr(e, stridx, sym.st_name); + if (s != NULL && strcmp(s, symbol) == 0) { + memcpy(symcopy, &sym, sizeof(*symcopy)); + if (si != NULL) + si->prs_id = i; + return (0); } } - - return (NULL); + return (1); } int proc_name2sym(struct proc_handle *p, const char *object, const char *symbol, - GElf_Sym *symcopy) + GElf_Sym *symcopy, prsyminfo_t *si) { 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; + uintptr_t off; + u_long symtabstridx = 0, dynsymstridx = 0; + int fd, error = -1; if ((map = proc_name2map(p, object)) == NULL) { DPRINTFX("ERROR: couldn't find object %s", object); @@ -460,46 +468,25 @@ proc_name2sym(struct proc_handle *p, const char *object, const char *symbol, 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))) { - 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)); - if (ehdr.e_type != ET_EXEC) - 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) + * First look up the symbol in the dynsymtab, and fall back to the + * symtab if the lookup fails. */ - 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; - } - } - } + error = lookup_name(e, dynsymscn, dynsymstridx, symbol, symcopy, si); + if (error == 0) + goto out; + + error = lookup_name(e, symtabscn, symtabstridx, symbol, symcopy, si); + if (error == 0) + goto 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: elf_end(e); err1: @@ -510,6 +497,23 @@ err0: return (error); } +ctf_file_t * +proc_name2ctf(struct proc_handle *p, const char *name) +{ +#ifndef NO_CTF + prmap_t *map; + int error; + + if ((map = proc_name2map(p, name)) == NULL) + return (NULL); + + return (ctf_open(map->pr_mapname, &error)); +#else + (void)p; + (void)name; + return (NULL); +#endif +} int proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, @@ -547,7 +551,7 @@ proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, scn = NULL; while ((scn = elf_nextscn(e, scn)) != NULL) { gelf_getshdr(scn, &shdr); - if (which == PR_SYMTAB && + if (which == PR_SYMTAB && shdr.sh_type == SHT_SYMTAB) { foundscn = scn; break; @@ -564,8 +568,7 @@ proc_iter_symbyaddr(struct proc_handle *p, const char *object, int which, DPRINTFX("ERROR: elf_getdata() failed: %s", elf_errmsg(-1)); goto err2; } - i = 0; - while (gelf_getsym(data, i++, &sym) != NULL) { + for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { if (GELF_ST_BIND(sym.st_info) == STB_LOCAL && (mask & BIND_LOCAL) == 0) continue; diff --git a/lib/libproc/test/Makefile b/lib/libproc/test/Makefile deleted file mode 100644 index e4d33f82ce44..000000000000 --- a/lib/libproc/test/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# $FreeBSD$ - -SUBDIR= t1-bkpt t2-name2map t3-name2sym - -.include <bsd.subdir.mk> diff --git a/lib/libproc/test/t1-bkpt/Makefile b/lib/libproc/test/t1-bkpt/Makefile deleted file mode 100644 index eb5b37f94936..000000000000 --- a/lib/libproc/test/t1-bkpt/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# $FreeBSD$ - -PROG= t1-bkpt - -SRCS= t1-bkpt.c - -LDADD= -lproc -lelf -lrtld_db -lutil -DPADD= ${LIBPROC} ${LIBELF} - -MAN= - -.include <bsd.prog.mk> diff --git a/lib/libproc/test/t1-bkpt/t1-bkpt.c b/lib/libproc/test/t1-bkpt/t1-bkpt.c deleted file mode 100644 index 1cd4d17f5612..000000000000 --- a/lib/libproc/test/t1-bkpt/t1-bkpt.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. - * - * This software was 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * 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 - * SUCH DAMAGE. - * - * $FreeBSD$ - */ -#include <sys/types.h> -#include <sys/wait.h> - -#include <assert.h> -#include <stdio.h> -#include <string.h> -#include <libproc.h> - -int __noinline -t1_bkpt_t() -{ - printf("TEST OK\n"); -} - -int -t1_bkpt_d() -{ - struct proc_handle *phdl; - char *targv[] = { "t1-bkpt-t", NULL}; - unsigned long saved; - - proc_create("./t1-bkpt", targv, NULL, NULL, &phdl); - assert(proc_bkptset(phdl, (uintptr_t)t1_bkpt_t, &saved) == 0); - proc_continue(phdl); - assert(proc_wstatus(phdl) == PS_STOP); - proc_bkptexec(phdl, saved); - proc_continue(phdl); - proc_wstatus(phdl); - proc_free(phdl); -} - - -int -main(int argc, char **argv) -{ - if (!strcmp(argv[0], "t1-bkpt-t")) - t1_bkpt_t(); - else - t1_bkpt_d(); -} - diff --git a/lib/libproc/test/t2-name2map/Makefile b/lib/libproc/test/t2-name2map/Makefile deleted file mode 100644 index 9002accf6904..000000000000 --- a/lib/libproc/test/t2-name2map/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# $FreeBSD$ - -PROG= t2-name2map - -SRCS= t2-name2map.c - -LDADD= -lproc -lelf -lrtld_db -lutil -DPADD= ${LIBPROC} ${LIBELF} - -MAN= - -.include <bsd.prog.mk> diff --git a/lib/libproc/test/t3-name2sym/Makefile b/lib/libproc/test/t3-name2sym/Makefile deleted file mode 100644 index 68e23c6199d9..000000000000 --- a/lib/libproc/test/t3-name2sym/Makefile +++ /dev/null @@ -1,12 +0,0 @@ -# $FreeBSD$ - -PROG= t3-name2sym - -SRCS= t3-name2sym.c - -LDADD= -lproc -lelf -lrtld_db -lutil -DPADD= ${LIBPROC} ${LIBELF} - -MAN= - -.include <bsd.prog.mk> diff --git a/lib/libproc/test/t3-name2sym/t3-name2sym.c b/lib/libproc/test/t3-name2sym/t3-name2sym.c deleted file mode 100644 index 0be8653d4a49..000000000000 --- a/lib/libproc/test/t3-name2sym/t3-name2sym.c +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. - * - * This software was 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * 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 - * SUCH DAMAGE. - * - * $FreeBSD$ - */ -#include <sys/types.h> -#include <assert.h> -#include <stdio.h> -#include <libproc.h> -#include <gelf.h> -#include <string.h> - -int -main(int argc, char *argv[]) -{ - prmap_t *map = NULL; - struct proc_handle *phdl; - GElf_Sym sym; - - proc_create("./t3-name2sym", argv, NULL, NULL, &phdl); - memset(&sym, 0, sizeof(sym)); - assert(proc_name2sym(phdl, "ld-elf.so.1", "r_debug_state", &sym) == 0); - printf("0x%lx\n", sym.st_value); - assert(proc_name2sym(phdl, "t3-name2sym", "main", &sym) == 0); - printf("0x%lx\n", sym.st_value); -} diff --git a/lib/libproc/tests/Makefile b/lib/libproc/tests/Makefile new file mode 100644 index 000000000000..1f98467df24f --- /dev/null +++ b/lib/libproc/tests/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +TESTSDIR= ${TESTSBASE}/lib/libproc + +ATF_TESTS_C+= proc_test + +PROGS= target_prog +SRCS_target_prog= target_prog.c +BINDIR_target_prog= ${TESTSDIR} + +LDADD+= -lelf -lproc -lrtld_db -lutil +DPADD+= ${LIBELF} ${LIBPROC} ${LIBRTLD_DB} ${LIBUTIL} + +# Ensure that symbols aren't stripped from the test program, as they're needed +# for testing symbol lookup. +STRIP= + +MAN= +WARNS?= 6 + +.include <bsd.test.mk> diff --git a/lib/libproc/tests/proc_test.c b/lib/libproc/tests/proc_test.c new file mode 100644 index 000000000000..0242b5b5ee96 --- /dev/null +++ b/lib/libproc/tests/proc_test.c @@ -0,0 +1,349 @@ +/*- + * Copyright (c) 2014 Mark Johnston <markj@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * 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 + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/wait.h> + +#include <libgen.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <atf-c.h> +#include <libelf.h> +#include <libproc.h> + +static const char *aout_object = "a.out"; +static const char *ldelf_object = "ld-elf.so.1"; +static const char *target_prog_file = "target_prog"; + +/* + * Run the test program. If the sig parameter is set to true, the test program + * will deliver SIGUSR1 to itself during execution. + */ +static struct proc_handle * +start_prog(const struct atf_tc *tc, bool sig) +{ + char *argv[3]; + struct proc_handle *phdl; + int error; + + asprintf(&argv[0], "%s/%s", atf_tc_get_config_var(tc, "srcdir"), + target_prog_file); + ATF_REQUIRE(argv[0] != NULL); + + if (sig) { + argv[1] = strdup("-s"); + argv[2] = NULL; + } else { + argv[1] = NULL; + } + + error = proc_create(argv[0], argv, NULL, NULL, &phdl); + ATF_REQUIRE_EQ_MSG(error, 0, "failed to run '%s'", target_prog_file); + ATF_REQUIRE(phdl != NULL); + + free(argv[0]); + free(argv[1]); + + return (phdl); +} + +static void +set_bkpt(struct proc_handle *phdl, uintptr_t addr, u_long *saved) +{ + int error; + + error = proc_bkptset(phdl, addr, saved); + ATF_REQUIRE_EQ_MSG(error, 0, "failed to set breakpoint at 0x%jx", + (uintmax_t)addr); +} + +static void +remove_bkpt(struct proc_handle *phdl, uintptr_t addr, u_long val) +{ + int error; + + error = proc_bkptdel(phdl, addr, val); + ATF_REQUIRE_EQ_MSG(error, 0, + "failed to delete breakpoint at 0x%jx", (uintmax_t)addr); + + error = proc_regset(phdl, REG_PC, addr); + ATF_REQUIRE_EQ_MSG(error, 0, "failed to reset program counter"); +} + +/* + * Wait for the specified process to hit a breakpoint at the specified symbol. + */ +static void +verify_bkpt(struct proc_handle *phdl, GElf_Sym *sym, const char *symname, + const char *mapname) +{ + char mapbname[MAXPATHLEN], *name; + GElf_Sym tsym; + prmap_t *map; + size_t namesz; + u_long addr; + int error, state; + + state = proc_wstatus(phdl); + ATF_REQUIRE_EQ_MSG(state, PS_STOP, "process has state %d", state); + + /* Get the program counter and decrement it. */ + error = proc_regget(phdl, REG_PC, &addr); + ATF_REQUIRE_EQ_MSG(error, 0, "failed to obtain PC for '%s'", + target_prog_file); + proc_bkptregadj(&addr); + + /* + * Make sure the PC matches the expected value obtained from the symbol + * definition we looked up earlier. + */ + ATF_CHECK_EQ_MSG(addr, sym->st_value, + "program counter 0x%lx doesn't match expected value 0x%jx", + addr, (uintmax_t)sym->st_value); + + /* + * Ensure we can look up the r_debug_state symbol using its starting + * address and that the resulting symbol matches the one we found using + * a name lookup. + */ + namesz = strlen(symname) + 1; + name = malloc(namesz); + ATF_REQUIRE(name != NULL); + + error = proc_addr2sym(phdl, addr, name, namesz, &tsym); + ATF_REQUIRE_EQ_MSG(error, 0, "failed to look up symbol at 0x%lx", addr); + ATF_REQUIRE_EQ(memcmp(sym, &tsym, sizeof(*sym)), 0); + ATF_REQUIRE_EQ(strcmp(symname, name), 0); + free(name); + + map = proc_addr2map(phdl, addr); + ATF_REQUIRE_MSG(map != NULL, "failed to look up map for address 0x%lx", + addr); + basename_r(map->pr_mapname, mapbname); + ATF_REQUIRE_EQ_MSG(strcmp(mapname, mapbname), 0, + "expected map name '%s' doesn't match '%s'", mapname, mapbname); +} + +ATF_TC(map_alias_obj2map); +ATF_TC_HEAD(map_alias_obj2map, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Callers are supposed to be able to use \"a.out\" as an alias for " + "the program executable. Make sure that proc_obj2map() handles " + "this properly."); +} +ATF_TC_BODY(map_alias_obj2map, tc) +{ + struct proc_handle *phdl; + prmap_t *map1, *map2; + + phdl = start_prog(tc, false); + + /* Initialize the rtld_db handle. */ + (void)proc_rdagent(phdl); + + /* Ensure that "target_prog" and "a.out" return the same map. */ + map1 = proc_obj2map(phdl, target_prog_file); + ATF_REQUIRE_MSG(map1 != NULL, "failed to look up map for '%s'", + target_prog_file); + map2 = proc_obj2map(phdl, aout_object); + ATF_REQUIRE_MSG(map2 != NULL, "failed to look up map for '%s'", + aout_object); + ATF_CHECK_EQ(strcmp(map1->pr_mapname, map2->pr_mapname), 0); + + ATF_CHECK_EQ_MSG(proc_continue(phdl), 0, "failed to resume execution"); + + proc_free(phdl); +} + +ATF_TC(map_alias_name2map); +ATF_TC_HEAD(map_alias_name2map, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Callers are supposed to be able to use \"a.out\" as an alias for " + "the program executable. Make sure that proc_name2map() handles " + "this properly."); +} +ATF_TC_BODY(map_alias_name2map, tc) +{ + struct proc_handle *phdl; + prmap_t *map1, *map2; + + phdl = start_prog(tc, false); + + /* Initialize the rtld_db handle. */ + (void)proc_rdagent(phdl); + + /* Ensure that "target_prog" and "a.out" return the same map. */ + map1 = proc_name2map(phdl, target_prog_file); + ATF_REQUIRE_MSG(map1 != NULL, "failed to look up map for '%s'", + target_prog_file); + map2 = proc_name2map(phdl, aout_object); + ATF_REQUIRE_MSG(map2 != NULL, "failed to look up map for '%s'", + aout_object); + ATF_CHECK_EQ(strcmp(map1->pr_mapname, map2->pr_mapname), 0); + + ATF_CHECK_EQ_MSG(proc_continue(phdl), 0, "failed to resume execution"); + + proc_free(phdl); +} + +ATF_TC(map_alias_name2sym); +ATF_TC_HEAD(map_alias_name2sym, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Callers are supposed to be able to use \"a.out\" as an alias for " + "the program executable. Make sure that proc_name2sym() handles " + "this properly."); +} +ATF_TC_BODY(map_alias_name2sym, tc) +{ + GElf_Sym sym1, sym2; + prsyminfo_t si1, si2; + struct proc_handle *phdl; + int error; + + phdl = start_prog(tc, false); + + /* Initialize the rtld_db handle. */ + (void)proc_rdagent(phdl); + + /* + * Make sure that "target_prog:main" and "a.out:main" return the same + * symbol. + */ + error = proc_name2sym(phdl, target_prog_file, "main", &sym1, &si1); + ATF_REQUIRE_EQ_MSG(error, 0, "failed to look up 'main' via %s", + target_prog_file); + error = proc_name2sym(phdl, aout_object, "main", &sym2, &si2); + ATF_REQUIRE_EQ_MSG(error, 0, "failed to look up 'main' via %s", + aout_object); + + ATF_CHECK_EQ(memcmp(&sym1, &sym2, sizeof(sym1)), 0); + ATF_CHECK_EQ(si1.prs_id, si2.prs_id); + + ATF_CHECK_EQ_MSG(proc_continue(phdl), 0, "failed to resume execution"); + + proc_free(phdl); +} + +ATF_TC(symbol_lookup); +ATF_TC_HEAD(symbol_lookup, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Look up a couple of well-known symbols in the test program, place " + "breakpoints on them, and verify that we hit the breakpoints. Also " + "make sure that we can use the breakpoint address to look up the " + "corresponding symbol."); +} +ATF_TC_BODY(symbol_lookup, tc) +{ + GElf_Sym main_sym, r_debug_state_sym; + struct proc_handle *phdl; + u_long saved; + int error; + + phdl = start_prog(tc, false); + + error = proc_name2sym(phdl, target_prog_file, "main", &main_sym, NULL); + ATF_REQUIRE_EQ_MSG(error, 0, "failed to look up 'main'"); + + error = proc_name2sym(phdl, ldelf_object, "r_debug_state", + &r_debug_state_sym, NULL); + ATF_REQUIRE_EQ_MSG(error, 0, "failed to look up 'r_debug_state'"); + + set_bkpt(phdl, r_debug_state_sym.st_value, &saved); + ATF_CHECK_EQ_MSG(proc_continue(phdl), 0, "failed to resume execution"); + verify_bkpt(phdl, &r_debug_state_sym, "r_debug_state", ldelf_object); + remove_bkpt(phdl, r_debug_state_sym.st_value, saved); + + set_bkpt(phdl, main_sym.st_value, &saved); + ATF_CHECK_EQ_MSG(proc_continue(phdl), 0, "failed to resume execution"); + verify_bkpt(phdl, &main_sym, "main", target_prog_file); + remove_bkpt(phdl, main_sym.st_value, saved); + + ATF_CHECK_EQ_MSG(proc_continue(phdl), 0, "failed to resume execution"); + + proc_free(phdl); +} + +ATF_TC(signal_forward); +ATF_TC_HEAD(signal_forward, tc) +{ + atf_tc_set_md_var(tc, "descr", + "Run the test program in a mode which causes it to send a signal " + "to itself. Make sure that we intercept the signal and that " + "proc_continue() forwards it to the process."); +} +ATF_TC_BODY(signal_forward, tc) +{ + struct proc_handle *phdl; + int state, status; + + phdl = start_prog(tc, true); + ATF_CHECK_EQ_MSG(proc_continue(phdl), 0, "failed to resume execution"); + + /* The process should have been interrupted by a signal. */ + state = proc_wstatus(phdl); + ATF_REQUIRE_EQ_MSG(state, PS_STOP, "process has unexpected state %d", + state); + + /* Continue execution and allow the signal to be delivered. */ + ATF_CHECK_EQ_MSG(proc_continue(phdl), 0, "failed to resume execution"); + + /* + * Make sure the process exited with status 0. If it didn't receive the + * SIGUSR1 that it sent to itself, it'll exit with a non-zero exit + * status, causing the test to fail. + */ + state = proc_wstatus(phdl); + ATF_REQUIRE_EQ_MSG(state, PS_UNDEAD, "process has unexpected state %d", + state); + + status = proc_getwstat(phdl); + ATF_REQUIRE(status >= 0); + ATF_REQUIRE(WIFEXITED(status)); + ATF_REQUIRE_EQ(WEXITSTATUS(status), 0); + + proc_free(phdl); +} + +ATF_TP_ADD_TCS(tp) +{ + + ATF_TP_ADD_TC(tp, map_alias_obj2map); + ATF_TP_ADD_TC(tp, map_alias_name2map); + ATF_TP_ADD_TC(tp, map_alias_name2sym); + ATF_TP_ADD_TC(tp, symbol_lookup); + ATF_TP_ADD_TC(tp, signal_forward); + + return (atf_no_error()); +} diff --git a/lib/libproc/test/t2-name2map/t2-name2map.c b/lib/libproc/tests/target_prog.c index 825963d3f504..1edf0ded6122 100644 --- a/lib/libproc/test/t2-name2map/t2-name2map.c +++ b/lib/libproc/tests/target_prog.c @@ -1,46 +1,59 @@ -/* - * Copyright (c) 2010 The FreeBSD Foundation - * All rights reserved. - * - * This software was 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: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * 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 - * SUCH DAMAGE. +/*- + * Copyright (c) 2014 Mark Johnston <markj@FreeBSD.org> + * All rights reserved. * - * $FreeBSD$ + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * 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 + * SUCH DAMAGE. */ -#include <sys/types.h> -#include <assert.h> -#include <stdio.h> -#include <libproc.h> +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static volatile sig_atomic_t saw; + +static void +usr1(int sig __unused) +{ + + saw = 1; +} int -main(int argc, char *argv[]) +main(int argc, char **argv) { - prmap_t *map = NULL; - struct proc_handle *phdl; - proc_create("./t2-name2map", argv, NULL, NULL, &phdl); - map = proc_name2map(phdl, "ld-elf.so.1"); - assert(map); + if (argc == 1) + return (EXIT_SUCCESS); + if (argc == 2 && strcmp(argv[1], "-s") == 0) { + if (signal(SIGUSR1, usr1) == SIG_ERR) + err(1, "signal"); + if (kill(getpid(), SIGUSR1) != 0) + err(1, "kill"); + return (saw == 1 ? EXIT_SUCCESS : EXIT_FAILURE); + } + return (EXIT_FAILURE); } |
