libproc: Add proc_getmodel().

This is used by libdtrace to determine the data model of target processes.
This allows for the creation of pid provider probes in 32-bit processes on
amd64.

MFC after:	1 month
This commit is contained in:
Mark Johnston 2016-07-30 03:09:23 +00:00
parent 700244a246
commit 4808a67805
7 changed files with 110 additions and 45 deletions

View File

@ -24,7 +24,7 @@ LIBADD+= cxxrt
LIBADD+= supcplusplus
.endif
LIBADD+= elf rtld_db util
LIBADD+= elf procstat rtld_db util
.if ${MK_CDDL} != "no"
LIBADD+= ctf

View File

@ -33,18 +33,22 @@
#include "libproc.h"
struct procstat;
struct proc_handle {
pid_t pid; /* Process ID. */
int flags; /* Process flags. */
int status; /* Process status (PS_*). */
int wstat; /* Process wait status. */
int model; /* Process data model. */
rd_agent_t *rdap; /* librtld_db agent */
rd_loadobj_t *rdobjs; /* Array of loaded objects. */
size_t rdobjsz; /* Array size. */
size_t nobjs; /* Num. objects currently loaded. */
rd_loadobj_t *rdexec; /* rdobj for program executable. */
struct lwpstatus lwps; /* Process status. */
char execname[MAXPATHLEN]; /* Path to program executable. */
struct procstat *procstat; /* libprocstat handle. */
char execpath[MAXPATHLEN]; /* Path to program executable. */
};
#ifdef DEBUG

View File

@ -113,6 +113,9 @@ typedef struct lwpstatus {
#define FLTBPT -1
} lwpstatus_t;
#define PR_MODEL_ILP32 1
#define PR_MODEL_LP64 2
/* Function prototype definitions. */
__BEGIN_DECLS
@ -136,6 +139,7 @@ int proc_name2sym(struct proc_handle *, const char *, const char *,
struct ctf_file *proc_name2ctf(struct proc_handle *, const char *);
int proc_setflags(struct proc_handle *, int);
int proc_state(struct proc_handle *);
int proc_getmodel(struct proc_handle *);
pid_t proc_getpid(struct proc_handle *);
int proc_wstatus(struct proc_handle *);
int proc_getwstat(struct proc_handle *);

View File

@ -28,6 +28,7 @@
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#include <sys/wait.h>
#include <err.h>
@ -38,57 +39,103 @@
#include <string.h>
#include <unistd.h>
#include <libelf.h>
#include <libprocstat.h>
#include "_libproc.h"
static int proc_init(pid_t, int, int, struct proc_handle *);
static int getelfclass(int);
static int proc_init(pid_t, int, int, struct proc_handle **);
static int
proc_init(pid_t pid, int flags, int status, struct proc_handle *phdl)
getelfclass(int fd)
{
int mib[4], error;
size_t len;
GElf_Ehdr ehdr;
Elf *e;
int class;
class = ELFCLASSNONE;
if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
goto out;
if (gelf_getehdr(e, &ehdr) == NULL)
goto out;
class = ehdr.e_ident[EI_CLASS];
out:
(void)elf_end(e);
return (class);
}
static int
proc_init(pid_t pid, int flags, int status, struct proc_handle **pphdl)
{
struct kinfo_proc *kp;
struct proc_handle *phdl;
int error, class, count, fd;
*pphdl = NULL;
if ((phdl = malloc(sizeof(*phdl))) == NULL)
return (ENOMEM);
memset(phdl, 0, sizeof(*phdl));
phdl->pid = pid;
phdl->flags = flags;
phdl->status = status;
phdl->procstat = procstat_open_sysctl();
if (phdl->procstat == NULL)
return (ENOMEM);
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = pid;
len = sizeof(phdl->execname);
if (sysctl(mib, 4, phdl->execname, &len, NULL, 0) != 0) {
error = errno;
DPRINTF("ERROR: cannot get pathname for child process %d", pid);
/* Obtain a path to the executable. */
if ((kp = procstat_getprocs(phdl->procstat, KERN_PROC_PID, pid,
&count)) == NULL)
return (ENOMEM);
error = procstat_getpathname(phdl->procstat, kp, phdl->execpath,
sizeof(phdl->execpath));
procstat_freeprocs(phdl->procstat, kp);
if (error != 0)
return (error);
}
if (len == 0)
phdl->execname[0] = '\0';
return (0);
/* Use it to determine the data model for the process. */
if ((fd = open(phdl->execpath, O_RDONLY)) < 0) {
error = errno;
goto out;
}
class = getelfclass(fd);
switch (class) {
case ELFCLASS64:
phdl->model = PR_MODEL_LP64;
break;
case ELFCLASS32:
phdl->model = PR_MODEL_ILP32;
break;
case ELFCLASSNONE:
default:
error = EINVAL;
break;
}
(void)close(fd);
out:
*pphdl = phdl;
return (error);
}
int
proc_attach(pid_t pid, int flags, struct proc_handle **pphdl)
{
struct proc_handle *phdl;
int error = 0;
int status;
int error, status;
if (pid == 0 || pid == getpid())
return (EINVAL);
if (elf_version(EV_CURRENT) == EV_NONE)
return (ENOENT);
/*
* Allocate memory for the process handle, a structure containing
* all things related to the process.
*/
if ((phdl = malloc(sizeof(struct proc_handle))) == NULL)
return (ENOMEM);
elf_version(EV_CURRENT);
error = proc_init(pid, flags, PS_RUN, phdl);
error = proc_init(pid, flags, PS_RUN, &phdl);
if (error != 0)
goto out;
@ -106,16 +153,17 @@ proc_attach(pid_t pid, int flags, struct proc_handle **pphdl)
}
/* Check for an unexpected status. */
if (WIFSTOPPED(status) == 0)
if (!WIFSTOPPED(status))
DPRINTFX("ERROR: child process %d status 0x%x", pid, status);
else
phdl->status = PS_STOP;
out:
if (error)
if (error && phdl != NULL) {
proc_free(phdl);
else
*pphdl = phdl;
phdl = NULL;
}
*pphdl = phdl;
return (error);
}
@ -128,14 +176,8 @@ proc_create(const char *file, char * const *argv, proc_child_func *pcf,
int status;
pid_t pid;
/*
* Allocate memory for the process handle, a structure containing
* all things related to the process.
*/
if ((phdl = malloc(sizeof(struct proc_handle))) == NULL)
return (ENOMEM);
elf_version(EV_CURRENT);
if (elf_version(EV_CURRENT) == EV_NONE)
return (ENOENT);
/* Fork a new process. */
if ((pid = vfork()) == -1)
@ -153,9 +195,10 @@ proc_create(const char *file, char * const *argv, proc_child_func *pcf,
/* Couldn't execute the file. */
_exit(2);
/* NOTREACHED */
} else {
/* The parent owns the process handle. */
error = proc_init(pid, 0, PS_IDLE, phdl);
error = proc_init(pid, 0, PS_IDLE, &phdl);
if (error != 0)
goto bad;
@ -167,7 +210,7 @@ proc_create(const char *file, char * const *argv, proc_child_func *pcf,
}
/* Check for an unexpected status. */
if (WIFSTOPPED(status) == 0) {
if (!WIFSTOPPED(status)) {
error = errno;
DPRINTFX("ERROR: child process %d status 0x%x", pid, status);
goto bad;
@ -175,15 +218,19 @@ proc_create(const char *file, char * const *argv, proc_child_func *pcf,
phdl->status = PS_STOP;
}
bad:
if (error)
if (error && phdl != NULL) {
proc_free(phdl);
else
*pphdl = phdl;
phdl = NULL;
}
*pphdl = phdl;
return (error);
}
void
proc_free(struct proc_handle *phdl)
{
if (phdl->procstat != NULL)
procstat_close(phdl->procstat);
free(phdl);
}

View File

@ -49,7 +49,7 @@ map_iter(const rd_loadobj_t *lop, void *arg)
if (phdl->rdobjs == NULL)
return (-1);
}
if (strcmp(lop->rdl_path, phdl->execname) == 0 &&
if (strcmp(lop->rdl_path, phdl->execpath) == 0 &&
(lop->rdl_prot & RD_RDL_X) != 0)
phdl->rdexec = &phdl->rdobjs[phdl->nobjs];
memcpy(&phdl->rdobjs[phdl->nobjs++], lop, sizeof(*lop));

View File

@ -139,6 +139,16 @@ proc_getpid(struct proc_handle *phdl)
return (phdl->pid);
}
int
proc_getmodel(struct proc_handle *phdl)
{
if (phdl == NULL)
return (-1);
return (phdl->model);
}
int
proc_wstatus(struct proc_handle *phdl)
{

View File

@ -255,7 +255,7 @@ _DP_proc= supcplusplus
.if ${MK_CDDL} != "no"
_DP_proc+= ctf
.endif
_DP_proc+= elf rtld_db util
_DP_proc+= elf procstat rtld_db util
_DP_mp= crypto
_DP_memstat= kvm
_DP_magic= z