librtld_db: Use the auxv to figure out where to look up loader symbols.
Previously, librtld_db just hardcoded /libexec/ld-elf.so, which isn't correct for processes that aren't using the native ABI. With this change, librtld_db can be used to inspect non-native processes; in particular, dtrace -c now works for 32-bit executables on amd64. MFC after: 1 month
This commit is contained in:
parent
e04d8562b6
commit
a7e13d50fe
@ -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
|
# cddl/lib/libdtrace requires lib/libproc and lib/librtld_db; it's only built
|
||||||
# on select architectures though (see cddl/lib/Makefile)
|
# on select architectures though (see cddl/lib/Makefile)
|
||||||
.if ${MACHINE_CPUARCH} != "sparc64"
|
.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
|
.endif
|
||||||
|
|
||||||
.if ${MK_CRYPT} != "no"
|
.if ${MK_CRYPT} != "no"
|
||||||
|
@ -14,4 +14,6 @@ CFLAGS+= -I${.CURDIR}
|
|||||||
# Avoid circular dependency, we only need the libproc.h header here.
|
# Avoid circular dependency, we only need the libproc.h header here.
|
||||||
CFLAGS+= -I${.CURDIR:H}/libproc
|
CFLAGS+= -I${.CURDIR:H}/libproc
|
||||||
|
|
||||||
|
LIBADD+= elf procstat
|
||||||
|
|
||||||
.include <bsd.lib.mk>
|
.include <bsd.lib.mk>
|
||||||
|
@ -25,20 +25,30 @@
|
|||||||
* 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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/cdefs.h>
|
#include <sys/cdefs.h>
|
||||||
__FBSDID("$FreeBSD$");
|
__FBSDID("$FreeBSD$");
|
||||||
|
|
||||||
#include <machine/_inttypes.h>
|
#include <sys/param.h>
|
||||||
#include <sys/types.h>
|
#include <sys/queue.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
#include <sys/user.h>
|
#include <sys/user.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <machine/elf.h>
|
||||||
|
|
||||||
|
#include <libelf.h>
|
||||||
#include <libproc.h>
|
#include <libproc.h>
|
||||||
|
#include <libprocstat.h>
|
||||||
#include <libutil.h>
|
#include <libutil.h>
|
||||||
|
|
||||||
#include "rtld_db.h"
|
#include "rtld_db.h"
|
||||||
@ -55,6 +65,8 @@ void
|
|||||||
rd_delete(rd_agent_t *rdap)
|
rd_delete(rd_agent_t *rdap)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (rdap->rda_procstat != NULL)
|
||||||
|
procstat_close(rdap->rda_procstat);
|
||||||
free(rdap);
|
free(rdap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,9 +158,10 @@ rd_init(int version)
|
|||||||
rd_err_e
|
rd_err_e
|
||||||
rd_loadobj_iter(rd_agent_t *rdap, rl_iter_f *cb, void *clnt_data)
|
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;
|
struct kinfo_vmentry *kves, *kve;
|
||||||
|
rd_loadobj_t rdl;
|
||||||
|
rd_err_e ret;
|
||||||
|
int cnt, i, lastvn;
|
||||||
|
|
||||||
DPRINTF("%s\n", __func__);
|
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");
|
warn("ERROR: kinfo_getvmmap() failed");
|
||||||
return (RD_ERR);
|
return (RD_ERR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = RD_OK;
|
||||||
|
lastvn = 0;
|
||||||
for (i = 0; i < cnt; i++) {
|
for (i = 0; i < cnt; i++) {
|
||||||
kve = kves + i;
|
kve = kves + i;
|
||||||
if (kve->kve_type == KVME_TYPE_VNODE)
|
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)
|
if (kve->kve_protection & KVME_PROT_EXEC)
|
||||||
rdl.rdl_prot |= RD_RDL_X;
|
rdl.rdl_prot |= RD_RDL_X;
|
||||||
strlcpy(rdl.rdl_path, kves[lastvn].kve_path,
|
strlcpy(rdl.rdl_path, kves[lastvn].kve_path,
|
||||||
sizeof(rdl.rdl_path));
|
sizeof(rdl.rdl_path));
|
||||||
(*cb)(&rdl, clnt_data);
|
if ((*cb)(&rdl, clnt_data) != 0) {
|
||||||
|
ret = RD_ERR;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
free(kves);
|
free(kves);
|
||||||
|
return (ret);
|
||||||
return (RD_OK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -195,13 +213,18 @@ rd_new(struct proc_handle *php)
|
|||||||
{
|
{
|
||||||
rd_agent_t *rdap;
|
rd_agent_t *rdap;
|
||||||
|
|
||||||
rdap = malloc(sizeof(rd_agent_t));
|
rdap = malloc(sizeof(*rdap));
|
||||||
if (rdap) {
|
if (rdap == NULL)
|
||||||
memset(rdap, 0, sizeof(rd_agent_t));
|
return (NULL);
|
||||||
rdap->rda_php = php;
|
|
||||||
rd_reset(rdap);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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);
|
return (rdap);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,24 +254,136 @@ rd_plt_resolution(rd_agent_t *rdap, uintptr_t pc, struct proc *proc,
|
|||||||
return (RD_ERR);
|
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_err_e
|
||||||
rd_reset(rd_agent_t *rdap)
|
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",
|
kp = NULL;
|
||||||
&sym, NULL) < 0)
|
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);
|
return (RD_ERR);
|
||||||
DPRINTF("found r_debug_state at 0x%lx\n", (unsigned long)sym.st_value);
|
assert(count == 1);
|
||||||
rdap->rda_preinit_addr = sym.st_value;
|
|
||||||
rdap->rda_dlactivity_addr = sym.st_value;
|
|
||||||
|
|
||||||
if (proc_name2sym(rdap->rda_php, "ld-elf.so.1", "_r_debug_postinit",
|
auxv = procstat_getauxv(rdap->rda_procstat, kp, &count);
|
||||||
&sym, NULL) < 0)
|
if (auxv == NULL)
|
||||||
return (RD_ERR);
|
goto err;
|
||||||
DPRINTF("found _r_debug_postinit at 0x%lx\n",
|
|
||||||
(unsigned long)sym.st_value);
|
|
||||||
rdap->rda_postinit_addr = sym.st_value;
|
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -33,9 +33,6 @@
|
|||||||
#define _RTLD_DB_H_
|
#define _RTLD_DB_H_
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/cdefs.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
|
|
||||||
#define RD_VERSION 1
|
#define RD_VERSION 1
|
||||||
|
|
||||||
@ -49,11 +46,17 @@ typedef enum {
|
|||||||
RD_NOMAPS
|
RD_NOMAPS
|
||||||
} rd_err_e;
|
} rd_err_e;
|
||||||
|
|
||||||
|
/* XXX struct rd_agent should be private. */
|
||||||
|
struct procstat;
|
||||||
|
|
||||||
typedef struct rd_agent {
|
typedef struct rd_agent {
|
||||||
struct proc_handle *rda_php;
|
struct proc_handle *rda_php;
|
||||||
|
|
||||||
uintptr_t rda_dlactivity_addr;
|
uintptr_t rda_dlactivity_addr;
|
||||||
uintptr_t rda_preinit_addr;
|
uintptr_t rda_preinit_addr;
|
||||||
uintptr_t rda_postinit_addr;
|
uintptr_t rda_postinit_addr;
|
||||||
|
|
||||||
|
struct procstat *rda_procstat;
|
||||||
} rd_agent_t;
|
} rd_agent_t;
|
||||||
|
|
||||||
typedef struct rd_loadobj {
|
typedef struct rd_loadobj {
|
||||||
|
@ -243,6 +243,7 @@ _DP_radius= md
|
|||||||
.else
|
.else
|
||||||
_DP_radius= crypto
|
_DP_radius= crypto
|
||||||
.endif
|
.endif
|
||||||
|
_DP_rtld_db= elf procstat
|
||||||
_DP_procstat= kvm util elf
|
_DP_procstat= kvm util elf
|
||||||
.if ${MK_CXX} == "yes"
|
.if ${MK_CXX} == "yes"
|
||||||
.if ${MK_LIBCPLUSPLUS} != "no"
|
.if ${MK_LIBCPLUSPLUS} != "no"
|
||||||
|
Loading…
Reference in New Issue
Block a user