Print this page
3946 ::gcore
Reviewed by: Adam Leventhal <ahl@delphix.com>
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
@@ -23,10 +23,11 @@
* Use is subject to license terms.
*/
/*
* Copyright 2012 DEY Storage Systems, Inc. All rights reserved.
* Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright (c) 2013 by Delphix. All rights reserved.
*/
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/sysmacros.h>
@@ -109,40 +110,223 @@
}
return (n - resid);
}
+/*ARGSUSED*/
static ssize_t
-Pread_core(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr)
+Pread_core(struct ps_prochandle *P, void *buf, size_t n, uintptr_t addr,
+ void *data)
{
return (core_rw(P, buf, n, addr, pread64));
}
+/*ARGSUSED*/
static ssize_t
-Pwrite_core(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr)
+Pwrite_core(struct ps_prochandle *P, const void *buf, size_t n, uintptr_t addr,
+ void *data)
{
return (core_rw(P, (void *)buf, n, addr,
(ssize_t (*)(int, void *, size_t, off64_t)) pwrite64));
}
-static const ps_rwops_t P_core_ops = { Pread_core, Pwrite_core };
+/*ARGSUSED*/
+static int
+Pcred_core(struct ps_prochandle *P, prcred_t *pcrp, int ngroups, void *data)
+{
+ core_info_t *core = data;
+ if (core->core_cred != NULL) {
+ /*
+ * Avoid returning more supplementary group data than the
+ * caller has allocated in their buffer. We expect them to
+ * check pr_ngroups afterward and potentially call us again.
+ */
+ ngroups = MIN(ngroups, core->core_cred->pr_ngroups);
+
+ (void) memcpy(pcrp, core->core_cred,
+ sizeof (prcred_t) + (ngroups - 1) * sizeof (gid_t));
+
+ return (0);
+ }
+
+ errno = ENODATA;
+ return (-1);
+}
+
+/*ARGSUSED*/
+static int
+Ppriv_core(struct ps_prochandle *P, prpriv_t **pprv, void *data)
+{
+ core_info_t *core = data;
+
+ if (core->core_priv == NULL) {
+ errno = ENODATA;
+ return (-1);
+ }
+
+ *pprv = malloc(core->core_priv_size);
+ if (*pprv == NULL) {
+ return (-1);
+ }
+
+ (void) memcpy(*pprv, core->core_priv, core->core_priv_size);
+ return (0);
+}
+
+/*ARGSUSED*/
+static const psinfo_t *
+Ppsinfo_core(struct ps_prochandle *P, psinfo_t *psinfo, void *data)
+{
+ return (&P->psinfo);
+}
+
+/*ARGSUSED*/
+static void
+Pfini_core(struct ps_prochandle *P, void *data)
+{
+ core_info_t *core = data;
+
+ if (core != NULL) {
+ extern void __priv_free_info(void *);
+ lwp_info_t *nlwp, *lwp = list_next(&core->core_lwp_head);
+ int i;
+
+ for (i = 0; i < core->core_nlwp; i++, lwp = nlwp) {
+ nlwp = list_next(lwp);
+#ifdef __sparc
+ if (lwp->lwp_gwins != NULL)
+ free(lwp->lwp_gwins);
+ if (lwp->lwp_xregs != NULL)
+ free(lwp->lwp_xregs);
+ if (lwp->lwp_asrs != NULL)
+ free(lwp->lwp_asrs);
+#endif
+ free(lwp);
+ }
+
+ if (core->core_platform != NULL)
+ free(core->core_platform);
+ if (core->core_uts != NULL)
+ free(core->core_uts);
+ if (core->core_cred != NULL)
+ free(core->core_cred);
+ if (core->core_priv != NULL)
+ free(core->core_priv);
+ if (core->core_privinfo != NULL)
+ __priv_free_info(core->core_privinfo);
+ if (core->core_ppii != NULL)
+ free(core->core_ppii);
+ if (core->core_zonename != NULL)
+ free(core->core_zonename);
+#if defined(__i386) || defined(__amd64)
+ if (core->core_ldt != NULL)
+ free(core->core_ldt);
+#endif
+
+ free(core);
+ }
+}
+
+/*ARGSUSED*/
+static char *
+Pplatform_core(struct ps_prochandle *P, char *s, size_t n, void *data)
+{
+ core_info_t *core = data;
+
+ if (core->core_platform == NULL) {
+ errno = ENODATA;
+ return (NULL);
+ }
+ (void) strncpy(s, core->core_platform, n - 1);
+ s[n - 1] = '\0';
+ return (s);
+}
+
+/*ARGSUSED*/
+static int
+Puname_core(struct ps_prochandle *P, struct utsname *u, void *data)
+{
+ core_info_t *core = data;
+
+ if (core->core_uts == NULL) {
+ errno = ENODATA;
+ return (-1);
+ }
+ (void) memcpy(u, core->core_uts, sizeof (struct utsname));
+ return (0);
+}
+
+/*ARGSUSED*/
+static char *
+Pzonename_core(struct ps_prochandle *P, char *s, size_t n, void *data)
+{
+ core_info_t *core = data;
+
+ if (core->core_zonename == NULL) {
+ errno = ENODATA;
+ return (NULL);
+ }
+ (void) strlcpy(s, core->core_zonename, n);
+ return (s);
+}
+
+#if defined(__i386) || defined(__amd64)
+/*ARGSUSED*/
+static int
+Pldt_core(struct ps_prochandle *P, struct ssd *pldt, int nldt, void *data)
+{
+ core_info_t *core = data;
+
+ if (pldt == NULL || nldt == 0)
+ return (core->core_nldt);
+
+ if (core->core_ldt != NULL) {
+ nldt = MIN(nldt, core->core_nldt);
+
+ (void) memcpy(pldt, core->core_ldt,
+ nldt * sizeof (struct ssd));
+
+ return (nldt);
+ }
+
+ errno = ENODATA;
+ return (-1);
+}
+#endif
+
+static const ps_ops_t P_core_ops = {
+ .pop_pread = Pread_core,
+ .pop_pwrite = Pwrite_core,
+ .pop_cred = Pcred_core,
+ .pop_priv = Ppriv_core,
+ .pop_psinfo = Ppsinfo_core,
+ .pop_fini = Pfini_core,
+ .pop_platform = Pplatform_core,
+ .pop_uname = Puname_core,
+ .pop_zonename = Pzonename_core,
+#if defined(__i386) || defined(__amd64)
+ .pop_ldt = Pldt_core
+#endif
+};
+
/*
* Return the lwp_info_t for the given lwpid. If no such lwpid has been
* encountered yet, allocate a new structure and return a pointer to it.
* Create a list of lwp_info_t structures sorted in decreasing lwp_id order.
*/
static lwp_info_t *
lwpid2info(struct ps_prochandle *P, lwpid_t id)
{
- lwp_info_t *lwp = list_next(&P->core->core_lwp_head);
+ core_info_t *core = P->data;
+ lwp_info_t *lwp = list_next(&core->core_lwp_head);
lwp_info_t *next;
uint_t i;
- for (i = 0; i < P->core->core_nlwp; i++, lwp = list_next(lwp)) {
+ for (i = 0; i < core->core_nlwp; i++, lwp = list_next(lwp)) {
if (lwp->lwp_id == id) {
- P->core->core_lwp = lwp;
+ core->core_lwp = lwp;
return (lwp);
}
if (lwp->lwp_id < id) {
break;
}
@@ -153,12 +337,12 @@
return (NULL);
list_link(lwp, next);
lwp->lwp_id = id;
- P->core->core_lwp = lwp;
- P->core->core_nlwp++;
+ core->core_lwp = lwp;
+ core->core_nlwp++;
return (lwp);
}
/*
@@ -173,11 +357,13 @@
static int
note_pstatus(struct ps_prochandle *P, size_t nbytes)
{
#ifdef _LP64
- if (P->core->core_dmodel == PR_MODEL_ILP32) {
+ core_info_t *core = P->data;
+
+ if (core->core_dmodel == PR_MODEL_ILP32) {
pstatus32_t ps32;
if (nbytes < sizeof (pstatus32_t) ||
read(P->asfd, &ps32, sizeof (ps32)) != sizeof (ps32))
goto err;
@@ -205,11 +391,13 @@
{
lwp_info_t *lwp;
lwpstatus_t lps;
#ifdef _LP64
- if (P->core->core_dmodel == PR_MODEL_ILP32) {
+ core_info_t *core = P->data;
+
+ if (core->core_dmodel == PR_MODEL_ILP32) {
lwpstatus32_t l32;
if (nbytes < sizeof (lwpstatus32_t) ||
read(P->asfd, &l32, sizeof (l32)) != sizeof (l32))
goto err;
@@ -244,11 +432,13 @@
static int
note_psinfo(struct ps_prochandle *P, size_t nbytes)
{
#ifdef _LP64
- if (P->core->core_dmodel == PR_MODEL_ILP32) {
+ core_info_t *core = P->data;
+
+ if (core->core_dmodel == PR_MODEL_ILP32) {
psinfo32_t ps32;
if (nbytes < sizeof (psinfo32_t) ||
read(P->asfd, &ps32, sizeof (ps32)) != sizeof (ps32))
goto err;
@@ -276,11 +466,13 @@
{
lwp_info_t *lwp;
lwpsinfo_t lps;
#ifdef _LP64
- if (P->core->core_dmodel == PR_MODEL_ILP32) {
+ core_info_t *core = P->data;
+
+ if (core->core_dmodel == PR_MODEL_ILP32) {
lwpsinfo32_t l32;
if (nbytes < sizeof (lwpsinfo32_t) ||
read(P->asfd, &l32, sizeof (l32)) != sizeof (l32))
goto err;
@@ -326,35 +518,37 @@
}
static int
note_platform(struct ps_prochandle *P, size_t nbytes)
{
+ core_info_t *core = P->data;
char *plat;
- if (P->core->core_platform != NULL)
+ if (core->core_platform != NULL)
return (0); /* Already seen */
if (nbytes != 0 && ((plat = malloc(nbytes + 1)) != NULL)) {
if (read(P->asfd, plat, nbytes) != nbytes) {
dprintf("Pgrab_core: failed to read NT_PLATFORM\n");
free(plat);
return (-1);
}
plat[nbytes - 1] = '\0';
- P->core->core_platform = plat;
+ core->core_platform = plat;
}
return (0);
}
static int
note_utsname(struct ps_prochandle *P, size_t nbytes)
{
+ core_info_t *core = P->data;
size_t ubytes = sizeof (struct utsname);
struct utsname *utsp;
- if (P->core->core_uts != NULL || nbytes < ubytes)
+ if (core->core_uts != NULL || nbytes < ubytes)
return (0); /* Already seen or bad size */
if ((utsp = malloc(ubytes)) == NULL)
return (-1);
@@ -370,35 +564,37 @@
dprintf("uts.release = \"%s\"\n", utsp->release);
dprintf("uts.version = \"%s\"\n", utsp->version);
dprintf("uts.machine = \"%s\"\n", utsp->machine);
}
- P->core->core_uts = utsp;
+ core->core_uts = utsp;
return (0);
}
static int
note_content(struct ps_prochandle *P, size_t nbytes)
{
+ core_info_t *core = P->data;
core_content_t content;
- if (sizeof (P->core->core_content) != nbytes)
+ if (sizeof (core->core_content) != nbytes)
return (-1);
if (read(P->asfd, &content, sizeof (content)) != sizeof (content))
return (-1);
- P->core->core_content = content;
+ core->core_content = content;
dprintf("core content = %llx\n", content);
return (0);
}
static int
note_cred(struct ps_prochandle *P, size_t nbytes)
{
+ core_info_t *core = P->data;
prcred_t *pcrp;
int ngroups;
const size_t min_size = sizeof (prcred_t) - sizeof (gid_t);
/*
@@ -405,11 +601,11 @@
* We allow for prcred_t notes that are actually smaller than a
* prcred_t since the last member isn't essential if there are
* no group memberships. This allows for more flexibility when it
* comes to slightly malformed -- but still valid -- notes.
*/
- if (P->core->core_cred != NULL || nbytes < min_size)
+ if (core->core_cred != NULL || nbytes < min_size)
return (0); /* Already seen or bad size */
ngroups = (nbytes - min_size) / sizeof (gid_t);
nbytes = sizeof (prcred_t) + (ngroups - 1) * sizeof (gid_t);
@@ -426,22 +622,23 @@
dprintf("pr_ngroups = %d; resetting to %d based on note size\n",
pcrp->pr_ngroups, ngroups);
pcrp->pr_ngroups = ngroups;
}
- P->core->core_cred = pcrp;
+ core->core_cred = pcrp;
return (0);
}
#if defined(__i386) || defined(__amd64)
static int
note_ldt(struct ps_prochandle *P, size_t nbytes)
{
+ core_info_t *core = P->data;
struct ssd *pldt;
uint_t nldt;
- if (P->core->core_ldt != NULL || nbytes < sizeof (struct ssd))
+ if (core->core_ldt != NULL || nbytes < sizeof (struct ssd))
return (0); /* Already seen or bad size */
nldt = nbytes / sizeof (struct ssd);
nbytes = nldt * sizeof (struct ssd);
@@ -452,22 +649,23 @@
dprintf("Pgrab_core: failed to read NT_LDT\n");
free(pldt);
return (-1);
}
- P->core->core_ldt = pldt;
- P->core->core_nldt = nldt;
+ core->core_ldt = pldt;
+ core->core_nldt = nldt;
return (0);
}
#endif /* __i386 */
static int
note_priv(struct ps_prochandle *P, size_t nbytes)
{
+ core_info_t *core = P->data;
prpriv_t *pprvp;
- if (P->core->core_priv != NULL || nbytes < sizeof (prpriv_t))
+ if (core->core_priv != NULL || nbytes < sizeof (prpriv_t))
return (0); /* Already seen or bad size */
if ((pprvp = malloc(nbytes)) == NULL)
return (-1);
@@ -475,22 +673,23 @@
dprintf("Pgrab_core: failed to read NT_PRPRIV\n");
free(pprvp);
return (-1);
}
- P->core->core_priv = pprvp;
- P->core->core_priv_size = nbytes;
+ core->core_priv = pprvp;
+ core->core_priv_size = nbytes;
return (0);
}
static int
note_priv_info(struct ps_prochandle *P, size_t nbytes)
{
+ core_info_t *core = P->data;
extern void *__priv_parse_info();
priv_impl_info_t *ppii;
- if (P->core->core_privinfo != NULL ||
+ if (core->core_privinfo != NULL ||
nbytes < sizeof (priv_impl_info_t))
return (0); /* Already seen or bad size */
if ((ppii = malloc(nbytes)) == NULL)
return (-1);
@@ -500,21 +699,22 @@
dprintf("Pgrab_core: failed to read NT_PRPRIVINFO\n");
free(ppii);
return (-1);
}
- P->core->core_privinfo = __priv_parse_info(ppii);
- P->core->core_ppii = ppii;
+ core->core_privinfo = __priv_parse_info(ppii);
+ core->core_ppii = ppii;
return (0);
}
static int
note_zonename(struct ps_prochandle *P, size_t nbytes)
{
+ core_info_t *core = P->data;
char *zonename;
- if (P->core->core_zonename != NULL)
+ if (core->core_zonename != NULL)
return (0); /* Already seen */
if (nbytes != 0) {
if ((zonename = malloc(nbytes)) == NULL)
return (-1);
@@ -522,11 +722,11 @@
dprintf("Pgrab_core: failed to read NT_ZONENAME\n");
free(zonename);
return (-1);
}
zonename[nbytes - 1] = '\0';
- P->core->core_zonename = zonename;
+ core->core_zonename = zonename;
}
return (0);
}
@@ -534,11 +734,13 @@
note_auxv(struct ps_prochandle *P, size_t nbytes)
{
size_t n, i;
#ifdef _LP64
- if (P->core->core_dmodel == PR_MODEL_ILP32) {
+ core_info_t *core = P->data;
+
+ if (core->core_dmodel == PR_MODEL_ILP32) {
auxv32_t *a32;
n = nbytes / sizeof (auxv32_t);
nbytes = n * sizeof (auxv32_t);
a32 = alloca(nbytes);
@@ -592,11 +794,12 @@
#ifdef __sparc
static int
note_xreg(struct ps_prochandle *P, size_t nbytes)
{
- lwp_info_t *lwp = P->core->core_lwp;
+ core_info_t *core = P->data;
+ lwp_info_t *lwp = core->core_lwp;
size_t xbytes = sizeof (prxregset_t);
prxregset_t *xregs;
if (lwp == NULL || lwp->lwp_xregs != NULL || nbytes < xbytes)
return (0); /* No lwp yet, already seen, or bad size */
@@ -615,11 +818,12 @@
}
static int
note_gwindows(struct ps_prochandle *P, size_t nbytes)
{
- lwp_info_t *lwp = P->core->core_lwp;
+ core_info_t *core = P->data;
+ lwp_info_t *lwp = core->core_lwp;
if (lwp == NULL || lwp->lwp_gwins != NULL || nbytes == 0)
return (0); /* No lwp yet or already seen or no data */
if ((lwp->lwp_gwins = malloc(sizeof (gwindows_t))) == NULL)
@@ -630,11 +834,11 @@
* actually saved, we just read up to the minimum of the note size
* and the size of the gwindows_t type. It doesn't matter if the read
* fails since we have to zero out gwindows first anyway.
*/
#ifdef _LP64
- if (P->core->core_dmodel == PR_MODEL_ILP32) {
+ if (core->core_dmodel == PR_MODEL_ILP32) {
gwindows32_t g32;
(void) memset(&g32, 0, sizeof (g32));
(void) read(P->asfd, &g32, MIN(nbytes, sizeof (g32)));
gwindows_32_to_n(&g32, lwp->lwp_gwins);
@@ -652,11 +856,12 @@
#ifdef __sparcv9
static int
note_asrs(struct ps_prochandle *P, size_t nbytes)
{
- lwp_info_t *lwp = P->core->core_lwp;
+ core_info_t *core = P->data;
+ lwp_info_t *lwp = core->core_lwp;
int64_t *asrs;
if (lwp == NULL || lwp->lwp_asrs != NULL || nbytes < sizeof (asrset_t))
return (0); /* No lwp yet, already seen, or bad size */
@@ -677,11 +882,13 @@
static int
note_spymaster(struct ps_prochandle *P, size_t nbytes)
{
#ifdef _LP64
- if (P->core->core_dmodel == PR_MODEL_ILP32) {
+ core_info_t *core = P->data;
+
+ if (core->core_dmodel == PR_MODEL_ILP32) {
psinfo32_t ps32;
if (nbytes < sizeof (psinfo32_t) ||
read(P->asfd, &ps32, sizeof (ps32)) != sizeof (ps32))
goto err;
@@ -828,10 +1035,11 @@
* PT_LOAD program header. We fill in more information on the mapping later.
*/
static int
core_add_mapping(struct ps_prochandle *P, GElf_Phdr *php)
{
+ core_info_t *core = P->data;
prmap_t pmap;
dprintf("mapping base %llx filesz %llu memsz %llu offset %llu\n",
(u_longlong_t)php->p_vaddr, (u_longlong_t)php->p_filesz,
(u_longlong_t)php->p_memsz, (u_longlong_t)php->p_offset);
@@ -843,11 +1051,11 @@
* If Pgcore() or elfcore() fail to write a mapping, they will set
* PF_SUNW_FAILURE in the Phdr and try to stash away the errno for us.
*/
if (php->p_flags & PF_SUNW_FAILURE) {
core_report_mapping(P, php);
- } else if (php->p_filesz != 0 && php->p_offset >= P->core->core_size) {
+ } else if (php->p_filesz != 0 && php->p_offset >= core->core_size) {
Perror_printf(P, "core file may be corrupt -- data for mapping "
"at %p is missing\n", (void *)(uintptr_t)php->p_vaddr);
dprintf("core file may be corrupt -- data for mapping "
"at %p is missing\n", (void *)(uintptr_t)php->p_vaddr);
}
@@ -1479,10 +1687,11 @@
* and attempt to construct a symbol table for the load object.
*/
static int
core_iter_mapping(const rd_loadobj_t *rlp, struct ps_prochandle *P)
{
+ core_info_t *core = P->data;
char lname[PATH_MAX], buf[PATH_MAX];
file_info_t *fp;
map_info_t *mp;
if (Pread_string(P, lname, PATH_MAX, (off_t)rlp->rl_nameaddr) <= 0) {
@@ -1506,19 +1715,19 @@
* file_info_new() will try to use its section headers to
* identify any other mappings that belong to this load object.
*/
if ((fp = mp->map_file) == NULL &&
(fp = file_info_new(P, mp)) == NULL) {
- P->core->core_errno = errno;
+ core->core_errno = errno;
dprintf("failed to malloc mapping data\n");
return (0); /* Abort */
}
fp->file_map = mp;
/* Create a local copy of the load object representation */
if ((fp->file_lo = calloc(1, sizeof (rd_loadobj_t))) == NULL) {
- P->core->core_errno = errno;
+ core->core_errno = errno;
dprintf("failed to malloc mapping data\n");
return (0); /* Abort */
}
*fp->file_lo = *rlp;
@@ -1781,10 +1990,11 @@
*/
struct ps_prochandle *
Pfgrab_core(int core_fd, const char *aout_path, int *perr)
{
struct ps_prochandle *P;
+ core_info_t *core_info;
map_info_t *stk_mp, *brk_mp;
const char *execname;
char *interp;
int i, notes, pagesize;
uintptr_t addr, base_addr;
@@ -1846,11 +2056,11 @@
P->statfd = -1;
P->agentctlfd = -1;
P->agentstatfd = -1;
P->zoneroot = NULL;
P->info_valid = 1;
- P->ops = &P_core_ops;
+ Pinit_ops(&P->ops, &P_core_ops);
Pinitsym(P);
/*
* Fstat and open the core file and make sure it is a valid ELF core.
@@ -1865,32 +2075,33 @@
/*
* Allocate and initialize a core_info_t to hang off the ps_prochandle
* structure. We keep all core-specific information in this structure.
*/
- if ((P->core = calloc(1, sizeof (core_info_t))) == NULL) {
+ if ((core_info = calloc(1, sizeof (core_info_t))) == NULL) {
*perr = G_STRANGE;
goto err;
}
- list_link(&P->core->core_lwp_head, NULL);
- P->core->core_size = stbuf.st_size;
+ P->data = core_info;
+ list_link(&core_info->core_lwp_head, NULL);
+ core_info->core_size = stbuf.st_size;
/*
* In the days before adjustable core file content, this was the
* default core file content. For new core files, this value will
* be overwritten by the NT_CONTENT note section.
*/
- P->core->core_content = CC_CONTENT_STACK | CC_CONTENT_HEAP |
+ core_info->core_content = CC_CONTENT_STACK | CC_CONTENT_HEAP |
CC_CONTENT_DATA | CC_CONTENT_RODATA | CC_CONTENT_ANON |
CC_CONTENT_SHANON;
switch (core.e_hdr.e_ident[EI_CLASS]) {
case ELFCLASS32:
- P->core->core_dmodel = PR_MODEL_ILP32;
+ core_info->core_dmodel = PR_MODEL_ILP32;
break;
case ELFCLASS64:
- P->core->core_dmodel = PR_MODEL_LP64;
+ core_info->core_dmodel = PR_MODEL_LP64;
break;
default:
*perr = G_FORMAT;
goto err;
}
@@ -2112,11 +2323,11 @@
dp->d_size != 0) {
dprintf(".interp = <%s>\n", (char *)dp->d_buf);
interp = dp->d_buf;
} else if (base_addr != (uintptr_t)-1L) {
- if (P->core->core_dmodel == PR_MODEL_LP64)
+ if (core_info->core_dmodel == PR_MODEL_LP64)
interp = "/usr/lib/64/ld.so.1";
else
interp = "/usr/lib/ld.so.1";
dprintf(".interp section is missing or could not be read; "
@@ -2227,12 +2438,12 @@
if ((P->rap = rd_new(P)) != NULL) {
(void) rd_loadobj_iter(P->rap, (rl_iter_f *)
core_iter_mapping, P);
- if (P->core->core_errno != 0) {
- errno = P->core->core_errno;
+ if (core_info->core_errno != 0) {
+ errno = core_info->core_errno;
*perr = G_STRANGE;
goto err;
}
} else
dprintf("failed to initialize rtld_db agent\n");