diff --git a/Makefile.inc1 b/Makefile.inc1 index 1be9ad546bc6..8959b24e2bc7 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -2095,7 +2095,9 @@ cddl/lib/libctf__L: lib/libz__L # cddl/lib/libdtrace requires lib/libproc and lib/librtld_db; it's only built # on select architectures though (see cddl/lib/Makefile) .if ${MACHINE_CPUARCH} != "sparc64" -_prebuild_libs+= lib/libproc lib/librtld_db +_prebuild_libs+= lib/libprocstat lib/libproc lib/librtld_db +lib/libproc__L: lib/libprocstat__L +lib/librtld_db__L: lib/libprocstat__L .endif .if ${MK_CRYPT} != "no" diff --git a/lib/librtld_db/Makefile b/lib/librtld_db/Makefile index eec7970eaaa9..704140397f58 100644 --- a/lib/librtld_db/Makefile +++ b/lib/librtld_db/Makefile @@ -14,4 +14,6 @@ CFLAGS+= -I${.CURDIR} # Avoid circular dependency, we only need the libproc.h header here. CFLAGS+= -I${.CURDIR:H}/libproc +LIBADD+= elf procstat + .include diff --git a/lib/librtld_db/rtld_db.c b/lib/librtld_db/rtld_db.c index cd4377e12904..a05e7c003c30 100644 --- a/lib/librtld_db/rtld_db.c +++ b/lib/librtld_db/rtld_db.c @@ -25,20 +25,30 @@ * 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 __FBSDID("$FreeBSD$"); -#include -#include +#include +#include +#include #include +#include #include +#include +#include #include #include #include -#include +#include + +#include + +#include #include +#include #include #include "rtld_db.h" @@ -55,6 +65,8 @@ void rd_delete(rd_agent_t *rdap) { + if (rdap->rda_procstat != NULL) + procstat_close(rdap->rda_procstat); free(rdap); } @@ -146,9 +158,10 @@ rd_init(int version) rd_err_e rd_loadobj_iter(rd_agent_t *rdap, rl_iter_f *cb, void *clnt_data) { - int cnt, i, lastvn = 0; - rd_loadobj_t rdl; struct kinfo_vmentry *kves, *kve; + rd_loadobj_t rdl; + rd_err_e ret; + int cnt, i, lastvn; DPRINTF("%s\n", __func__); @@ -156,6 +169,9 @@ rd_loadobj_iter(rd_agent_t *rdap, rl_iter_f *cb, void *clnt_data) warn("ERROR: kinfo_getvmmap() failed"); return (RD_ERR); } + + ret = RD_OK; + lastvn = 0; for (i = 0; i < cnt; i++) { kve = kves + i; if (kve->kve_type == KVME_TYPE_VNODE) @@ -174,12 +190,14 @@ rd_loadobj_iter(rd_agent_t *rdap, rl_iter_f *cb, void *clnt_data) if (kve->kve_protection & KVME_PROT_EXEC) rdl.rdl_prot |= RD_RDL_X; strlcpy(rdl.rdl_path, kves[lastvn].kve_path, - sizeof(rdl.rdl_path)); - (*cb)(&rdl, clnt_data); + sizeof(rdl.rdl_path)); + if ((*cb)(&rdl, clnt_data) != 0) { + ret = RD_ERR; + break; + } } free(kves); - - return (RD_OK); + return (ret); } void @@ -195,13 +213,18 @@ rd_new(struct proc_handle *php) { rd_agent_t *rdap; - rdap = malloc(sizeof(rd_agent_t)); - if (rdap) { - memset(rdap, 0, sizeof(rd_agent_t)); - rdap->rda_php = php; - rd_reset(rdap); - } + rdap = malloc(sizeof(*rdap)); + if (rdap == NULL) + return (NULL); + memset(rdap, 0, sizeof(rd_agent_t)); + rdap->rda_php = php; + rdap->rda_procstat = procstat_open_sysctl(); + + if (rd_reset(rdap) != RD_OK) { + rd_delete(rdap); + rdap = NULL; + } return (rdap); } @@ -231,24 +254,136 @@ rd_plt_resolution(rd_agent_t *rdap, uintptr_t pc, struct proc *proc, return (RD_ERR); } +static int +rtld_syms(rd_agent_t *rdap, const char *rtldpath, u_long base) +{ + GElf_Shdr shdr; + GElf_Sym sym; + Elf *e; + Elf_Data *data; + Elf_Scn *scn; + const char *symname; + Elf64_Word strscnidx; + int fd, i, ret; + + ret = 1; + e = NULL; + + fd = open(rtldpath, O_RDONLY); + if (fd < 0) + goto err; + + if (elf_version(EV_CURRENT) == EV_NONE) + goto err; + e = elf_begin(fd, ELF_C_READ, NULL); + if (e == NULL) { + close(fd); + goto err; + } + + scn = NULL; + while ((scn = elf_nextscn(e, scn)) != NULL) { + gelf_getshdr(scn, &shdr); + if (shdr.sh_type == SHT_DYNSYM) + break; + } + if (scn == NULL) + goto err; + + strscnidx = shdr.sh_link; + data = elf_getdata(scn, NULL); + if (data == NULL) + goto err; + + for (i = 0; gelf_getsym(data, i, &sym) != NULL; i++) { + if (GELF_ST_TYPE(sym.st_info) != STT_FUNC || + GELF_ST_BIND(sym.st_info) != STB_GLOBAL) + continue; + symname = elf_strptr(e, strscnidx, sym.st_name); + if (symname == NULL) + continue; + + if (strcmp(symname, "r_debug_state") == 0) { + rdap->rda_preinit_addr = sym.st_value + base; + rdap->rda_dlactivity_addr = sym.st_value + base; + } else if (strcmp(symname, "_r_debug_postinit") == 0) { + rdap->rda_postinit_addr = sym.st_value + base; + } + } + + if (rdap->rda_preinit_addr != 0 && + rdap->rda_postinit_addr != 0 && + rdap->rda_dlactivity_addr != 0) + ret = 0; + +err: + if (e != NULL) + (void)elf_end(e); + if (fd >= 0) + (void)close(fd); + return (ret); +} + rd_err_e rd_reset(rd_agent_t *rdap) { - GElf_Sym sym; + struct kinfo_proc *kp; + struct kinfo_vmentry *kve; + Elf_Auxinfo *auxv; + const char *rtldpath; + u_long base; + rd_err_e rderr; + int count, i; - if (proc_name2sym(rdap->rda_php, "ld-elf.so.1", "r_debug_state", - &sym, NULL) < 0) + kp = NULL; + auxv = NULL; + kve = NULL; + rderr = RD_ERR; + + kp = procstat_getprocs(rdap->rda_procstat, KERN_PROC_PID, + proc_getpid(rdap->rda_php), &count); + if (kp == NULL) return (RD_ERR); - DPRINTF("found r_debug_state at 0x%lx\n", (unsigned long)sym.st_value); - rdap->rda_preinit_addr = sym.st_value; - rdap->rda_dlactivity_addr = sym.st_value; + assert(count == 1); - if (proc_name2sym(rdap->rda_php, "ld-elf.so.1", "_r_debug_postinit", - &sym, NULL) < 0) - return (RD_ERR); - DPRINTF("found _r_debug_postinit at 0x%lx\n", - (unsigned long)sym.st_value); - rdap->rda_postinit_addr = sym.st_value; + auxv = procstat_getauxv(rdap->rda_procstat, kp, &count); + if (auxv == NULL) + goto err; - return (RD_OK); + base = 0; + for (i = 0; i < count; i++) { + if (auxv[i].a_type == AT_BASE) { + base = auxv[i].a_un.a_val; + break; + } + } + if (i == count) + goto err; + + rtldpath = NULL; + kve = procstat_getvmmap(rdap->rda_procstat, kp, &count); + if (kve == NULL) + goto err; + for (i = 0; i < count; i++) { + if (kve[i].kve_start == base) { + rtldpath = kve[i].kve_path; + break; + } + } + if (i == count) + goto err; + + if (rtld_syms(rdap, rtldpath, base) != 0) + goto err; + + rderr = RD_OK; + +err: + if (kve != NULL) + procstat_freevmmap(rdap->rda_procstat, kve); + if (auxv != NULL) + procstat_freeauxv(rdap->rda_procstat, auxv); + if (kp != NULL) + procstat_freeprocs(rdap->rda_procstat, kp); + return (rderr); } diff --git a/lib/librtld_db/rtld_db.h b/lib/librtld_db/rtld_db.h index 9dd53f0095e5..ea7242cbc697 100644 --- a/lib/librtld_db/rtld_db.h +++ b/lib/librtld_db/rtld_db.h @@ -33,9 +33,6 @@ #define _RTLD_DB_H_ #include -#include -#include - #define RD_VERSION 1 @@ -49,11 +46,17 @@ typedef enum { RD_NOMAPS } rd_err_e; +/* XXX struct rd_agent should be private. */ +struct procstat; + typedef struct rd_agent { struct proc_handle *rda_php; + uintptr_t rda_dlactivity_addr; uintptr_t rda_preinit_addr; uintptr_t rda_postinit_addr; + + struct procstat *rda_procstat; } rd_agent_t; typedef struct rd_loadobj { diff --git a/share/mk/src.libnames.mk b/share/mk/src.libnames.mk index 30fab90c6003..4eb0530535ce 100644 --- a/share/mk/src.libnames.mk +++ b/share/mk/src.libnames.mk @@ -243,6 +243,7 @@ _DP_radius= md .else _DP_radius= crypto .endif +_DP_rtld_db= elf procstat _DP_procstat= kvm util elf .if ${MK_CXX} == "yes" .if ${MK_LIBCPLUSPLUS} != "no"