Instead of generating all the 'note' sections twice (and hoping that the

'fast path' size on the first path matches the actual size on the second)
  save all the notes (mostly the cpu registers for all the LWPs) in
  malloced memory on the first pass.
Sanity check that the number of memory segments matches written matches
  the count obtained earlier. If gcore() is used they could differ.
  (Not sure that returning ENOMEM is ideal, but it is better than a crash.)
This commit is contained in:
dsl 2014-01-03 20:52:47 +00:00
parent ba1a5140ab
commit d773820bd9
4 changed files with 196 additions and 171 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: core_elf32.c,v 1.38 2014/01/03 15:15:02 dsl Exp $ */
/* $NetBSD: core_elf32.c,v 1.39 2014/01/03 20:52:47 dsl Exp $ */
/*
* Copyright (c) 2001 Wasabi Systems, Inc.
@ -40,7 +40,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(1, "$NetBSD: core_elf32.c,v 1.38 2014/01/03 15:15:02 dsl Exp $");
__KERNEL_RCSID(1, "$NetBSD: core_elf32.c,v 1.39 2014/01/03 20:52:47 dsl Exp $");
#ifdef _KERNEL_OPT
#include "opt_coredump.h"
@ -69,18 +69,33 @@ __KERNEL_RCSID(1, "$NetBSD: core_elf32.c,v 1.38 2014/01/03 15:15:02 dsl Exp $");
struct writesegs_state {
Elf_Phdr *psections;
off_t secoff;
size_t npsections;
};
/*
* We need to know how big the 'notes' are before we write the main header.
* To avoid problems with double-processing we save the data.
*/
struct note_buf {
struct note_buf *nb_next;
unsigned char nb_data[4096 - sizeof (void *)];
};
struct note_state {
struct note_buf *ns_first;
struct note_buf *ns_last;
unsigned int ns_count; /* Of full buffers */
unsigned int ns_offset; /* Write point in last buffer */
};
static int ELFNAMEEND(coredump_getseghdrs)(struct proc *,
struct uvm_coredump_state *);
static int ELFNAMEEND(coredump_notes)(struct proc *, struct lwp *,
struct coredump_iostate *, size_t *);
static int ELFNAMEEND(coredump_note)(struct proc *, struct lwp *,
struct coredump_iostate *, size_t *);
static int ELFNAMEEND(coredump_notes)(struct lwp *, struct note_state *);
static int ELFNAMEEND(coredump_note)(struct lwp *, struct note_state *);
/* The 'note' section names and data are always 4-byte aligned. */
#define ELFROUNDSIZE 4 /* XXX Should it be sizeof(Elf_Word)? */
#define elfround(x) roundup((x), ELFROUNDSIZE)
#define elf_process_read_regs CONCAT(process_read_regs, ELFSIZE)
#ifdef __HAVE_PROCESS_XFPREGS
@ -94,18 +109,31 @@ static int ELFNAMEEND(coredump_note)(struct proc *, struct lwp *,
int
ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie)
{
struct proc *p;
Elf_Ehdr ehdr;
Elf_Phdr phdr, *psections;
Elf_Phdr *psections;
size_t psectionssize;
int npsections;
struct writesegs_state ws;
off_t notestart, secstart, offset;
off_t notestart;
size_t notesize;
int error, i;
struct note_state ns;
struct note_buf *nb;
psections = NULL;
p = l->l_proc;
/* Get all of the notes (mostly all the registers). */
ns.ns_first = kmem_alloc(sizeof *ns.ns_first, KM_SLEEP);
ns.ns_last = ns.ns_first;
ns.ns_count = 0;
ns.ns_offset = 0;
error = ELFNAMEEND(coredump_notes)(l, &ns);
ns.ns_last->nb_next = NULL;
if (error)
goto out;
notesize = ns.ns_count * sizeof nb->nb_data + ns.ns_offset;
/*
* We have to make a total of 3 passes across the map:
*
@ -118,15 +146,11 @@ ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie)
*/
/* Pass 1: count the entries. */
npsections = uvm_coredump_count_segs(p);
/* Count the PT_NOTE section. */
npsections = uvm_coredump_count_segs(l->l_proc);
/* Allow for the PT_NOTE section. */
npsections++;
/* Get the size of the notes (mostly all the registers). */
error = ELFNAMEEND(coredump_notes)(p, l, NULL, &notesize);
if (error)
goto out;
/* Build the main elf header */
memset(&ehdr.e_ident[EI_PAD], 0, sizeof(ehdr.e_ident) - EI_PAD);
memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
#if ELFSIZE == 32
@ -164,20 +188,24 @@ ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie)
if (error)
goto out;
offset = sizeof(ehdr);
psectionssize = npsections * sizeof(*psections);
notestart = sizeof(ehdr) + psectionssize;
notestart = offset + sizeof(phdr) * npsections;
secstart = notestart + notesize;
psectionssize = npsections * sizeof(Elf_Phdr);
psections = kmem_zalloc(psectionssize, KM_SLEEP);
/* Pass 2: now find the P-section headers. */
ws.secoff = secstart;
ws.secoff = notestart + notesize;
ws.psections = psections;
error = uvm_coredump_walkmap(p, ELFNAMEEND(coredump_getseghdrs), &ws);
ws.npsections = npsections - 1;
error = uvm_coredump_walkmap(l->l_proc, ELFNAMEEND(coredump_getseghdrs),
&ws);
if (error)
goto out;
if (ws.npsections != 0) {
/* A section went away */
error = ENOMEM;
goto out;
}
/* Add the PT_NOTE header after the P-section headers. */
ws.psections->p_type = PT_NOTE;
@ -189,40 +217,35 @@ ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie)
ws.psections->p_flags = PF_R;
ws.psections->p_align = ELFROUNDSIZE;
/* Write the P-section headers followed by the PT_NOTR header */
error = coredump_write(cookie, UIO_SYSSPACE, psections,
npsections * sizeof(Elf_Phdr));
/* Write the P-section headers followed by the PT_NOTE header */
error = coredump_write(cookie, UIO_SYSSPACE, psections, psectionssize);
if (error)
goto out;
#ifdef DIAGNOSTIC
offset += npsections * sizeof(Elf_Phdr);
if (offset != notestart)
if (coredump_offset(cookie) != notestart)
panic("coredump: offset %lld != notestart %lld",
(long long) offset, (long long) notestart);
(long long) coredump_offset(cookie),
(long long) notestart);
#endif
/* Write out the notes. */
error = ELFNAMEEND(coredump_notes)(p, l, cookie, &notesize);
if (error)
goto out;
for (nb = ns.ns_first; nb != NULL; nb = nb->nb_next) {
error = coredump_write(cookie, UIO_SYSSPACE, nb->nb_data,
nb->nb_next == NULL ? ns.ns_offset : sizeof nb->nb_data);
if (error)
goto out;
}
#ifdef DIAGNOSTIC
offset += notesize;
if (offset != secstart)
panic("coredump: offset %lld != secstart %lld",
(long long) offset, (long long) secstart);
#endif
/* Pass 3: finally, write the sections themselves. */
/* Finally, write the sections themselves. */
for (i = 0; i < npsections - 1; i++) {
if (psections[i].p_filesz == 0)
continue;
#ifdef DIAGNOSTIC
if (offset != psections[i].p_offset)
if (coredump_offset(cookie) != psections[i].p_offset)
panic("coredump: offset %lld != p_offset[%d] %lld",
(long long) offset, i,
(long long) coredump_offset(cookie), i,
(long long) psections[i].p_filesz);
#endif
@ -231,15 +254,13 @@ ELFNAMEEND(coredump)(struct lwp *l, struct coredump_iostate *cookie)
psections[i].p_filesz);
if (error)
goto out;
#ifdef DIAGNOSTIC
offset += psections[i].p_filesz;
#endif
}
out:
if (psections)
kmem_free(psections, psectionssize);
for (; (nb = ns.ns_first) != NULL; ns.ns_first = nb->nb_next)
kmem_free(nb, sizeof *nb);
return (error);
}
@ -252,6 +273,11 @@ ELFNAMEEND(coredump_getseghdrs)(struct proc *p, struct uvm_coredump_state *us)
vaddr_t end;
int error;
/* Don't overrun if there are more sections */
if (ws->npsections == 0)
return ENOMEM;
ws->npsections--;
size = us->end - us->start;
realsize = us->realend - us->start;
end = us->realend;
@ -299,74 +325,65 @@ ELFNAMEEND(coredump_getseghdrs)(struct proc *p, struct uvm_coredump_state *us)
}
static int
ELFNAMEEND(coredump_notes)(struct proc *p, struct lwp *l,
struct coredump_iostate *iocookie, size_t *sizep)
ELFNAMEEND(coredump_notes)(struct lwp *l, struct note_state *ns)
{
struct proc *p;
struct netbsd_elfcore_procinfo cpi;
Elf_Nhdr nhdr;
size_t size, notesize;
int error;
struct lwp *l0;
sigset_t ss1, ss2;
size = 0;
p = l->l_proc;
/* First, write an elfcore_procinfo. */
notesize = sizeof(nhdr) + elfround(sizeof(ELF_NOTE_NETBSD_CORE_NAME)) +
elfround(sizeof(cpi));
if (iocookie) {
cpi.cpi_version = NETBSD_ELFCORE_PROCINFO_VERSION;
cpi.cpi_cpisize = sizeof(cpi);
cpi.cpi_signo = p->p_sigctx.ps_signo;
cpi.cpi_sigcode = p->p_sigctx.ps_code;
cpi.cpi_siglwp = p->p_sigctx.ps_lwp;
cpi.cpi_version = NETBSD_ELFCORE_PROCINFO_VERSION;
cpi.cpi_cpisize = sizeof(cpi);
cpi.cpi_signo = p->p_sigctx.ps_signo;
cpi.cpi_sigcode = p->p_sigctx.ps_code;
cpi.cpi_siglwp = p->p_sigctx.ps_lwp;
/*
* XXX This should be per-LWP.
*/
ss1 = p->p_sigpend.sp_set;
sigemptyset(&ss2);
LIST_FOREACH(l0, &p->p_lwps, l_sibling) {
sigplusset(&l0->l_sigpend.sp_set, &ss1);
sigplusset(&l0->l_sigmask, &ss2);
}
memcpy(&cpi.cpi_sigpend, &ss1, sizeof(cpi.cpi_sigpend));
memcpy(&cpi.cpi_sigmask, &ss2, sizeof(cpi.cpi_sigmask));
memcpy(&cpi.cpi_sigignore, &p->p_sigctx.ps_sigignore,
sizeof(cpi.cpi_sigignore));
memcpy(&cpi.cpi_sigcatch, &p->p_sigctx.ps_sigcatch,
sizeof(cpi.cpi_sigcatch));
cpi.cpi_pid = p->p_pid;
mutex_enter(proc_lock);
cpi.cpi_ppid = p->p_pptr->p_pid;
cpi.cpi_pgrp = p->p_pgid;
cpi.cpi_sid = p->p_session->s_sid;
mutex_exit(proc_lock);
cpi.cpi_ruid = kauth_cred_getuid(l->l_cred);
cpi.cpi_euid = kauth_cred_geteuid(l->l_cred);
cpi.cpi_svuid = kauth_cred_getsvuid(l->l_cred);
cpi.cpi_rgid = kauth_cred_getgid(l->l_cred);
cpi.cpi_egid = kauth_cred_getegid(l->l_cred);
cpi.cpi_svgid = kauth_cred_getsvgid(l->l_cred);
cpi.cpi_nlwps = p->p_nlwps;
(void)strncpy(cpi.cpi_name, p->p_comm, sizeof(cpi.cpi_name));
cpi.cpi_name[sizeof(cpi.cpi_name) - 1] = '\0';
nhdr.n_namesz = sizeof(ELF_NOTE_NETBSD_CORE_NAME);
nhdr.n_descsz = sizeof(cpi);
nhdr.n_type = ELF_NOTE_NETBSD_CORE_PROCINFO;
error = ELFNAMEEND(coredump_writenote)(p, iocookie, &nhdr,
ELF_NOTE_NETBSD_CORE_NAME "\0\0\0", &cpi);
if (error)
return (error);
/*
* XXX This should be per-LWP.
*/
ss1 = p->p_sigpend.sp_set;
sigemptyset(&ss2);
LIST_FOREACH(l0, &p->p_lwps, l_sibling) {
sigplusset(&l0->l_sigpend.sp_set, &ss1);
sigplusset(&l0->l_sigmask, &ss2);
}
memcpy(&cpi.cpi_sigpend, &ss1, sizeof(cpi.cpi_sigpend));
memcpy(&cpi.cpi_sigmask, &ss2, sizeof(cpi.cpi_sigmask));
memcpy(&cpi.cpi_sigignore, &p->p_sigctx.ps_sigignore,
sizeof(cpi.cpi_sigignore));
memcpy(&cpi.cpi_sigcatch, &p->p_sigctx.ps_sigcatch,
sizeof(cpi.cpi_sigcatch));
size += notesize;
cpi.cpi_pid = p->p_pid;
mutex_enter(proc_lock);
cpi.cpi_ppid = p->p_pptr->p_pid;
cpi.cpi_pgrp = p->p_pgid;
cpi.cpi_sid = p->p_session->s_sid;
mutex_exit(proc_lock);
cpi.cpi_ruid = kauth_cred_getuid(l->l_cred);
cpi.cpi_euid = kauth_cred_geteuid(l->l_cred);
cpi.cpi_svuid = kauth_cred_getsvuid(l->l_cred);
cpi.cpi_rgid = kauth_cred_getgid(l->l_cred);
cpi.cpi_egid = kauth_cred_getegid(l->l_cred);
cpi.cpi_svgid = kauth_cred_getsvgid(l->l_cred);
cpi.cpi_nlwps = p->p_nlwps;
(void)strncpy(cpi.cpi_name, p->p_comm, sizeof(cpi.cpi_name));
cpi.cpi_name[sizeof(cpi.cpi_name) - 1] = '\0';
nhdr.n_namesz = sizeof(ELF_NOTE_NETBSD_CORE_NAME);
nhdr.n_descsz = sizeof(cpi);
nhdr.n_type = ELF_NOTE_NETBSD_CORE_PROCINFO;
ELFNAMEEND(coredump_savenote)(ns, &nhdr, ELF_NOTE_NETBSD_CORE_NAME,
&cpi);
/* XXX Add hook for machdep per-proc notes. */
@ -374,10 +391,9 @@ ELFNAMEEND(coredump_notes)(struct proc *p, struct lwp *l,
* Now write the register info for the thread that caused the
* coredump.
*/
error = ELFNAMEEND(coredump_note)(p, l, iocookie, &notesize);
error = ELFNAMEEND(coredump_note)(l, ns);
if (error)
return (error);
size += notesize;
/*
* Now, for each LWP, write the register info and any other
@ -387,98 +403,100 @@ ELFNAMEEND(coredump_notes)(struct proc *p, struct lwp *l,
LIST_FOREACH(l0, &p->p_lwps, l_sibling) {
if (l0 == l) /* we've taken care of this thread */
continue;
error = ELFNAMEEND(coredump_note)(p, l0, iocookie, &notesize);
error = ELFNAMEEND(coredump_note)(l0, ns);
if (error)
return (error);
size += notesize;
}
*sizep = size;
return (0);
}
static int
ELFNAMEEND(coredump_note)(struct proc *p, struct lwp *l,
struct coredump_iostate *iocookie, size_t *sizep)
ELFNAMEEND(coredump_note)(struct lwp *l, struct note_state *ns)
{
Elf_Nhdr nhdr;
int size, notesize, error;
int error;
int namesize;
char name[64+ELFROUNDSIZE];
char name[64];
elf_reg intreg;
#ifdef PT_GETFPREGS
elf_fpreg freg;
size_t freglen;
#endif
size = 0;
snprintf(name, sizeof(name)-ELFROUNDSIZE, "%s@%d",
snprintf(name, sizeof(name), "%s@%d",
ELF_NOTE_NETBSD_CORE_NAME, l->l_lid);
namesize = strlen(name) + 1;
memset(name + namesize, 0, elfround(namesize) - namesize);
notesize = sizeof(nhdr) + elfround(namesize) + elfround(sizeof(intreg));
if (iocookie) {
error = elf_process_read_regs(l, &intreg);
if (error)
return (error);
error = elf_process_read_regs(l, &intreg);
if (error)
return (error);
nhdr.n_namesz = namesize;
nhdr.n_descsz = sizeof(intreg);
nhdr.n_type = PT_GETREGS;
nhdr.n_namesz = namesize;
nhdr.n_descsz = sizeof(intreg);
nhdr.n_type = PT_GETREGS;
error = ELFNAMEEND(coredump_writenote)(p, iocookie, &nhdr,
name, &intreg);
if (error)
return (error);
}
size += notesize;
ELFNAMEEND(coredump_savenote)(ns, &nhdr, name, &intreg);
#ifdef PT_GETFPREGS
notesize = sizeof(nhdr) + elfround(namesize) + elfround(sizeof(freg));
if (iocookie) {
size_t freglen = sizeof(freg);
freglen = sizeof(freg);
#ifdef __HAVE_PROCESS_XFPREGS
error = elf_process_read_xfpregs(l, &freg, &freglen);
error = elf_process_read_xfpregs(l, &freg, &freglen);
#else
error = elf_process_read_fpregs(l, &freg);
error = elf_process_read_fpregs(l, &freg);
#endif
if (error)
return (error);
if (error)
return (error);
nhdr.n_namesz = namesize;
nhdr.n_descsz = freglen;
nhdr.n_type = PT_GETFPREGS;
nhdr.n_namesz = namesize;
nhdr.n_descsz = freglen;
nhdr.n_type = PT_GETFPREGS;
error = ELFNAMEEND(coredump_writenote)(p, iocookie, &nhdr,
name, &freg);
if (error)
return (error);
}
size += notesize;
ELFNAMEEND(coredump_savenote)(ns, &nhdr, name, &freg);
#endif
*sizep = size;
/* XXX Add hook for machdep per-LWP notes. */
return (0);
}
int
ELFNAMEEND(coredump_writenote)(struct proc *p, struct coredump_iostate *cookie,
Elf_Nhdr *nhdr, const char *name, void *data)
static void
save_note_bytes(struct note_state *ns, const void *data, size_t len)
{
int error;
struct note_buf *nb = ns->ns_last;
size_t copylen;
unsigned char *wp;
error = coredump_write(cookie, UIO_SYSSPACE, nhdr, sizeof(*nhdr));
if (error)
return error;
/*
* Just copy the data into a buffer list.
* All but the last buffer is full.
*/
for (;;) {
copylen = min(len, sizeof nb->nb_data - ns->ns_offset);
wp = nb->nb_data + ns->ns_offset;
memcpy(wp, data, copylen);
if (copylen == len)
break;
nb->nb_next = kmem_alloc(sizeof *nb->nb_next, KM_SLEEP);
nb = nb->nb_next;
ns->ns_last = nb;
ns->ns_count++;
ns->ns_offset = 0;
len -= copylen;
data = (const unsigned char *)data + copylen;
}
error = coredump_write(cookie, UIO_SYSSPACE, name,
elfround(nhdr->n_namesz));
if (error)
return error;
while (copylen & (ELFROUNDSIZE - 1))
wp[copylen++] = 0;
return coredump_write(cookie, UIO_SYSSPACE, data, nhdr->n_descsz);
ns->ns_offset += copylen;
}
void
ELFNAMEEND(coredump_savenote)(struct note_state *ns, Elf_Nhdr *nhdr,
const char *name, void *data)
{
save_note_bytes(ns, nhdr, sizeof (*nhdr));
save_note_bytes(ns, name, nhdr->n_namesz);
save_note_bytes(ns, data, nhdr->n_descsz);
}
#else /* COREDUMP */

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_core.c,v 1.21 2014/01/01 18:57:16 dsl Exp $ */
/* $NetBSD: kern_core.c,v 1.22 2014/01/03 20:52:47 dsl Exp $ */
/*
* Copyright (c) 1982, 1986, 1989, 1991, 1993
@ -37,7 +37,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_core.c,v 1.21 2014/01/01 18:57:16 dsl Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_core.c,v 1.22 2014/01/03 20:52:47 dsl Exp $");
#include <sys/param.h>
#include <sys/vnode.h>
@ -303,10 +303,9 @@ coredump_buildname(struct proc *p, char *dst, const char *src, size_t len)
}
int
coredump_write(struct coredump_iostate *cookie, enum uio_seg segflg,
coredump_write(struct coredump_iostate *io, enum uio_seg segflg,
const void *data, size_t len)
{
struct coredump_iostate *io = cookie;
int error;
error = vn_rdwr(UIO_WRITE, io->io_vp, __UNCONST(data), len,
@ -324,3 +323,9 @@ coredump_write(struct coredump_iostate *cookie, enum uio_seg segflg,
io->io_offset += len;
return (0);
}
off_t
coredump_offset(struct coredump_iostate *io)
{
return io->io_offset;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: exec.h,v 1.143 2014/01/01 18:57:16 dsl Exp $ */
/* $NetBSD: exec.h,v 1.144 2014/01/03 20:52:47 dsl Exp $ */
/*-
* Copyright (c) 1992, 1993
@ -275,6 +275,7 @@ int exec_setup_stack (struct lwp *, struct exec_package *);
int coredump_write (struct coredump_iostate *, enum uio_seg,
const void *, size_t);
off_t coredump_offset (struct coredump_iostate *);
void exec_free_emul_arg (struct exec_package *);

View File

@ -1,4 +1,4 @@
/* $NetBSD: exec_elf.h,v 1.135 2014/01/03 16:54:48 dsl Exp $ */
/* $NetBSD: exec_elf.h,v 1.136 2014/01/03 20:52:47 dsl Exp $ */
/*-
* Copyright (c) 1994 The NetBSD Foundation, Inc.
@ -1250,6 +1250,7 @@ struct elf_args {
struct ps_strings;
struct coredump_iostate;
struct note_state;
#ifdef EXEC_ELF32
int exec_elf32_makecmds(struct lwp *, struct exec_package *);
@ -1257,8 +1258,8 @@ int elf32_copyargs(struct lwp *, struct exec_package *,
struct ps_strings *, char **, void *);
int coredump_elf32(struct lwp *, struct coredump_iostate *);
int coredump_writenote_elf32(struct proc *, struct coredump_iostate *,
Elf32_Nhdr *, const char *, void *);
void coredump_savenote_elf32(struct note_state *, Elf32_Nhdr *,
const char *, void *);
int elf32_check_header(Elf32_Ehdr *, int);
#endif
@ -1269,8 +1270,8 @@ int elf64_copyargs(struct lwp *, struct exec_package *,
struct ps_strings *, char **, void *);
int coredump_elf64(struct lwp *, struct coredump_iostate *);
int coredump_writenote_elf64(struct proc *, struct coredump_iostate *,
Elf64_Nhdr *, const char *, void *);
void coredump_savenote_elf64(struct note_state *, Elf64_Nhdr *,
const char *, void *);
int elf64_check_header(Elf64_Ehdr *, int);
#endif