diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2009-03-05 19:52:25 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2009-03-05 19:52:25 +0000 |
commit | 94b35ac8ba6c670755f5c398f89709d960544622 (patch) | |
tree | 0c62a6bd55e1b1b55e3b7f5f166d8d94a0c07777 /sys/kern | |
parent | 4939771421161444c79853f0ec2588d8aac2627e (diff) |
Make ELF platforms generate ELF core dumps. Somewhat based on code from
NetBSD.
ok kurt@, drahn@, miod@
Diffstat (limited to 'sys/kern')
-rw-r--r-- | sys/kern/exec_elf.c | 476 | ||||
-rw-r--r-- | sys/kern/init_main.c | 4 | ||||
-rw-r--r-- | sys/kern/kern_sig.c | 67 |
3 files changed, 535 insertions, 12 deletions
diff --git a/sys/kern/exec_elf.c b/sys/kern/exec_elf.c index 2a0083906db..59197aba4a9 100644 --- a/sys/kern/exec_elf.c +++ b/sys/kern/exec_elf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: exec_elf.c,v 1.67 2008/11/10 03:56:16 deraadt Exp $ */ +/* $OpenBSD: exec_elf.c,v 1.68 2009/03/05 19:52:24 kettenis Exp $ */ /* * Copyright (c) 1996 Per Fogelstrom @@ -31,6 +31,41 @@ * */ +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT 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 <sys/param.h> #include <sys/systm.h> #include <sys/kernel.h> @@ -40,10 +75,12 @@ #include <sys/mount.h> #include <sys/namei.h> #include <sys/vnode.h> +#include <sys/core.h> #include <sys/exec.h> #include <sys/exec_elf.h> #include <sys/exec_olf.h> #include <sys/file.h> +#include <sys/ptrace.h> #include <sys/syscall.h> #include <sys/signalvar.h> #include <sys/stat.h> @@ -90,6 +127,7 @@ int ELFNAME(check_header)(Elf_Ehdr *); int ELFNAME(read_from)(struct proc *, struct vnode *, u_long, caddr_t, int); void ELFNAME(load_psection)(struct exec_vmcmd_set *, struct vnode *, Elf_Phdr *, Elf_Addr *, Elf_Addr *, int *, int); +int ELFNAMEEND(coredump)(struct proc *, void *); extern char sigcode[], esigcode[]; #ifdef SYSCALL_DEBUG @@ -125,6 +163,7 @@ struct emul ELFNAMEEND(emul) = { ELFNAME(copyargs), setregs, ELFNAME2(exec,fixup), + ELFNAMEEND(coredump), sigcode, esigcode, EMUL_ENABLED | EMUL_NATIVE, @@ -881,3 +920,438 @@ out1: free(hph, M_TEMP); return error; } + +struct countsegs_state { + int npsections; +}; + +int ELFNAMEEND(coredump_countsegs)(struct proc *, void *, + struct uvm_coredump_state *); + +struct writesegs_state { + Elf_Phdr *psections; + off_t secoff; +}; + +int ELFNAMEEND(coredump_writeseghdrs)(struct proc *, void *, + struct uvm_coredump_state *); + +int ELFNAMEEND(coredump_notes)(struct proc *, void *, size_t *); +int ELFNAMEEND(coredump_note)(struct proc *, void *, size_t *); +int ELFNAMEEND(coredump_writenote)(struct proc *, void *, Elf_Note *, + const char *, void *); + +#define ELFROUNDSIZE 4 /* XXX Should it be sizeof(Elf_Word)? */ +#define elfround(x) roundup((x), ELFROUNDSIZE) + +int +ELFNAMEEND(coredump)(struct proc *p, void *cookie) +{ + Elf_Ehdr ehdr; + Elf_Phdr phdr, *psections; + struct countsegs_state cs; + struct writesegs_state ws; + off_t notestart, secstart, offset; + size_t notesize; + int error, i; + + psections = NULL; + /* + * We have to make a total of 3 passes across the map: + * + * 1. Count the number of map entries (the number of + * PT_LOAD sections). + * + * 2. Write the P-section headers. + * + * 3. Write the P-sections. + */ + + /* Pass 1: count the entries. */ + cs.npsections = 0; + error = uvm_coredump_walkmap(p, NULL, + ELFNAMEEND(coredump_countsegs), &cs); + if (error) + goto out; + + /* Count the PT_NOTE section. */ + cs.npsections++; + + /* Get the size of the notes. */ + error = ELFNAMEEND(coredump_notes)(p, NULL, ¬esize); + if (error) + goto out; + + memset(&ehdr, 0, sizeof(ehdr)); + memcpy(ehdr.e_ident, ELFMAG, SELFMAG); + ehdr.e_ident[EI_CLASS] = ELF_TARG_CLASS; + ehdr.e_ident[EI_DATA] = ELF_TARG_DATA; + ehdr.e_ident[EI_VERSION] = EV_CURRENT; + /* XXX Should be the OSABI/ABI version of the executable. */ + ehdr.e_ident[EI_OSABI] = ELFOSABI_SYSV; + ehdr.e_ident[EI_ABIVERSION] = 0; + ehdr.e_type = ET_CORE; + /* XXX This should be the e_machine of the executable. */ + ehdr.e_machine = ELF_TARG_MACH; + ehdr.e_version = EV_CURRENT; + ehdr.e_entry = 0; + ehdr.e_phoff = sizeof(ehdr); + ehdr.e_shoff = 0; + ehdr.e_flags = 0; + ehdr.e_ehsize = sizeof(ehdr); + ehdr.e_phentsize = sizeof(Elf_Phdr); + ehdr.e_phnum = cs.npsections; + ehdr.e_shentsize = 0; + ehdr.e_shnum = 0; + ehdr.e_shstrndx = 0; + + /* Write out the ELF header. */ + error = coredump_write(cookie, UIO_SYSSPACE, &ehdr, sizeof(ehdr)); + if (error) + goto out; + + offset = sizeof(ehdr); + + notestart = offset + sizeof(phdr) * cs.npsections; + secstart = notestart + notesize; + + psections = malloc(cs.npsections * sizeof(Elf_Phdr), + M_TEMP, M_WAITOK|M_ZERO); + + /* Pass 2: now write the P-section headers. */ + ws.secoff = secstart; + ws.psections = psections; + error = uvm_coredump_walkmap(p, cookie, + ELFNAMEEND(coredump_writeseghdrs), &ws); + if (error) + goto out; + + /* Write out the PT_NOTE header. */ + ws.psections->p_type = PT_NOTE; + ws.psections->p_offset = notestart; + ws.psections->p_vaddr = 0; + ws.psections->p_paddr = 0; + ws.psections->p_filesz = notesize; + ws.psections->p_memsz = 0; + ws.psections->p_flags = PF_R; + ws.psections->p_align = ELFROUNDSIZE; + + error = coredump_write(cookie, UIO_SYSSPACE, psections, + cs.npsections * sizeof(Elf_Phdr)); + if (error) + goto out; + +#ifdef DIAGNOSTIC + offset += cs.npsections * sizeof(Elf_Phdr); + if (offset != notestart) + panic("coredump: offset %lld != notestart %lld", + (long long) offset, (long long) notestart); +#endif + + /* Write out the notes. */ + error = ELFNAMEEND(coredump_notes)(p, cookie, ¬esize); + 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. */ + for (i = 0; i < cs.npsections - 1; i++) { + if (psections[i].p_filesz == 0) + continue; + +#ifdef DIAGNOSTIC + if (offset != psections[i].p_offset) + panic("coredump: offset %lld != p_offset[%d] %lld", + (long long) offset, i, + (long long) psections[i].p_filesz); +#endif + + error = coredump_write(cookie, UIO_USERSPACE, + (void *)(vaddr_t)psections[i].p_vaddr, + psections[i].p_filesz); + if (error) + goto out; + +#ifdef DIAGNOSTIC + offset += psections[i].p_filesz; +#endif + } + +out: + return (error); +} + +int +ELFNAMEEND(coredump_countsegs)(struct proc *p, void *iocookie, + struct uvm_coredump_state *us) +{ + struct countsegs_state *cs = us->cookie; + + cs->npsections++; + return (0); +} + +int +ELFNAMEEND(coredump_writeseghdrs)(struct proc *p, void *iocookie, + struct uvm_coredump_state *us) +{ + struct writesegs_state *ws = us->cookie; + Elf_Phdr phdr; + vsize_t size, realsize; + + size = us->end - us->start; + realsize = us->realend - us->start; + + phdr.p_type = PT_LOAD; + phdr.p_offset = ws->secoff; + phdr.p_vaddr = us->start; + phdr.p_paddr = 0; + phdr.p_filesz = realsize; + phdr.p_memsz = size; + phdr.p_flags = 0; + if (us->prot & VM_PROT_READ) + phdr.p_flags |= PF_R; + if (us->prot & VM_PROT_WRITE) + phdr.p_flags |= PF_W; + if (us->prot & VM_PROT_EXECUTE) + phdr.p_flags |= PF_X; + phdr.p_align = PAGE_SIZE; + + ws->secoff += phdr.p_filesz; + *ws->psections++ = phdr; + + return (0); +} + +int +ELFNAMEEND(coredump_notes)(struct proc *p, void *iocookie, size_t *sizep) +{ + struct ps_strings pss; + struct iovec iov; + struct uio uio; + struct elfcore_procinfo cpi; + Elf_Note nhdr; +#ifdef RTHREADS + struct proc *q; +#endif + size_t size, notesize; + int error; + + size = 0; + + /* First, write an elfcore_procinfo. */ + notesize = sizeof(nhdr) + elfround(sizeof("OpenBSD")) + + elfround(sizeof(cpi)); + if (iocookie) { + bzero(&cpi, sizeof(cpi)); + + cpi.cpi_version = ELFCORE_PROCINFO_VERSION; + cpi.cpi_cpisize = sizeof(cpi); + cpi.cpi_signo = p->p_sigacts->ps_sig; + cpi.cpi_sigcode = p->p_sigacts->ps_code; + + cpi.cpi_sigpend = p->p_siglist; + cpi.cpi_sigmask = p->p_sigmask; + cpi.cpi_sigignore = p->p_sigignore; + cpi.cpi_sigcatch = p->p_sigcatch; + + cpi.cpi_pid = p->p_pid; + cpi.cpi_ppid = p->p_pptr->p_pid; + cpi.cpi_pgrp = p->p_pgid; + cpi.cpi_sid = p->p_session->s_leader->p_pid; + + cpi.cpi_ruid = p->p_cred->p_ruid; + cpi.cpi_euid = p->p_ucred->cr_uid; + cpi.cpi_svuid = p->p_cred->p_svuid; + + cpi.cpi_rgid = p->p_cred->p_rgid; + cpi.cpi_egid = p->p_ucred->cr_gid; + cpi.cpi_svgid = p->p_cred->p_svgid; + + (void)strlcpy(cpi.cpi_name, p->p_comm, sizeof(cpi.cpi_name)); + + nhdr.namesz = sizeof("OpenBSD"); + nhdr.descsz = sizeof(cpi); + nhdr.type = NT_OPENBSD_PROCINFO; + + error = ELFNAMEEND(coredump_writenote)(p, iocookie, &nhdr, + "OpenBSD", &cpi); + if (error) + return (error); + } + size += notesize; + + /* Second, write an NT_OPENBSD_AUXV note. */ + notesize = sizeof(nhdr) + elfround(sizeof("OpenBSD")) + + elfround(p->p_emul->e_arglen * sizeof(char *)); + if (iocookie) { + iov.iov_base = &pss; + iov.iov_len = sizeof(pss); + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = (off_t)PS_STRINGS; + uio.uio_resid = sizeof(pss); + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_procp = NULL; + + error = uvm_io(&p->p_vmspace->vm_map, &uio, 0); + if (error) + return (error); + + if (pss.ps_envstr == NULL) + return (EIO); + + nhdr.namesz = sizeof("OpenBSD"); + nhdr.descsz = p->p_emul->e_arglen * sizeof(char *); + nhdr.type = NT_OPENBSD_AUXV; + + error = coredump_write(iocookie, UIO_SYSSPACE, + &nhdr, sizeof(nhdr)); + if (error) + return (error); + + error = coredump_write(iocookie, UIO_SYSSPACE, + "OpenBSD", elfround(nhdr.namesz)); + if (error) + return (error); + + error = coredump_write(iocookie, UIO_USERSPACE, + pss.ps_envstr + pss.ps_nenvstr + 1, nhdr.descsz); + if (error) + return (error); + } + size += notesize; + +#ifdef PT_WCOOKIE + notesize = sizeof(nhdr) + elfround(sizeof("OpenBSD")) + + elfround(sizeof(register_t)); + if (iocookie) { + register_t wcookie; + + nhdr.namesz = sizeof("OpenBSD"); + nhdr.descsz = sizeof(register_t); + nhdr.type = NT_OPENBSD_WCOOKIE; + + wcookie = process_get_wcookie(p); + error = ELFNAMEEND(coredump_writenote)(p, iocookie, &nhdr, + "OpenBSD", &wcookie); + if (error) + return (error); + } + size += notesize; +#endif + + /* + * Now write the register info for the thread that caused the + * coredump. + */ + error = ELFNAMEEND(coredump_note)(p, iocookie, ¬esize); + if (error) + return (error); + size += notesize; + +#ifdef RTHREADS + /* + * Now, for each thread, write the register info and any other + * per-thread notes. Since we're dumping core, we don't bother + * locking. + */ + TAILQ_FOREACH(q, &p->p_p->ps_threads, p_thr_link) { + if (q == p) /* we've taken care of this thread */ + continue; + error = ELFNAMEEND(coredump_note)(q, iocookie, ¬esize); + if (error) + return (error); + size += notesize; + } +#endif + + *sizep = size; + return (0); +} + +int +ELFNAMEEND(coredump_note)(struct proc *p, void *iocookie, size_t *sizep) +{ + Elf_Note nhdr; + int size, notesize, error; + int namesize; + char name[64+ELFROUNDSIZE]; + struct reg intreg; +#ifdef PT_GETFPREGS + struct fpreg freg; +#endif + + size = 0; + + snprintf(name, sizeof(name)-ELFROUNDSIZE, "%s@%d", + "OpenBSD", p->p_pid); + namesize = strlen(name) + 1; + memset(name + namesize, 0, elfround(namesize) - namesize); + + notesize = sizeof(nhdr) + elfround(namesize) + elfround(sizeof(intreg)); + if (iocookie) { + error = process_read_regs(p, &intreg); + if (error) + return (error); + + nhdr.namesz = namesize; + nhdr.descsz = sizeof(intreg); + nhdr.type = NT_OPENBSD_REGS; + + error = ELFNAMEEND(coredump_writenote)(p, iocookie, &nhdr, + name, &intreg); + if (error) + return (error); + + } + size += notesize; + +#ifdef PT_GETFPREGS + notesize = sizeof(nhdr) + elfround(namesize) + elfround(sizeof(freg)); + if (iocookie) { + error = process_read_fpregs(p, &freg); + if (error) + return (error); + + nhdr.namesz = namesize; + nhdr.descsz = sizeof(freg); + nhdr.type = NT_OPENBSD_FPREGS; + + error = ELFNAMEEND(coredump_writenote)(p, iocookie, &nhdr, + name, &freg); + if (error) + return (error); + } + size += notesize; +#endif + + *sizep = size; + /* XXX Add hook for machdep per-LWP notes. */ + return (0); +} + +int +ELFNAMEEND(coredump_writenote)(struct proc *p, void *cookie, Elf_Note *nhdr, + const char *name, void *data) +{ + int error; + + error = coredump_write(cookie, UIO_SYSSPACE, nhdr, sizeof(*nhdr)); + if (error) + return error; + + error = coredump_write(cookie, UIO_SYSSPACE, name, + elfround(nhdr->namesz)); + if (error) + return error; + + return coredump_write(cookie, UIO_SYSSPACE, data, nhdr->descsz); +} diff --git a/sys/kern/init_main.c b/sys/kern/init_main.c index b9b7010788f..b65ebbc9393 100644 --- a/sys/kern/init_main.c +++ b/sys/kern/init_main.c @@ -1,4 +1,4 @@ -/* $OpenBSD: init_main.c,v 1.157 2009/02/13 19:58:27 deraadt Exp $ */ +/* $OpenBSD: init_main.c,v 1.158 2009/03/05 19:52:24 kettenis Exp $ */ /* $NetBSD: init_main.c,v 1.84.4.1 1996/06/02 09:08:06 mrg Exp $ */ /* @@ -39,6 +39,7 @@ */ #include <sys/param.h> +#include <sys/core.h> #include <sys/filedesc.h> #include <sys/file.h> #include <sys/errno.h> @@ -163,6 +164,7 @@ struct emul emul_native = { copyargs, setregs, NULL, + coredump_trad, sigcode, esigcode, EMUL_ENABLED | EMUL_NATIVE, diff --git a/sys/kern/kern_sig.c b/sys/kern/kern_sig.c index ed52c212eca..15053a45b20 100644 --- a/sys/kern/kern_sig.c +++ b/sys/kern/kern_sig.c @@ -1,4 +1,4 @@ -/* $OpenBSD: kern_sig.c,v 1.102 2009/01/29 22:18:06 guenther Exp $ */ +/* $OpenBSD: kern_sig.c,v 1.103 2009/03/05 19:52:24 kettenis Exp $ */ /* $NetBSD: kern_sig.c,v 1.54 1996/04/22 01:38:32 christos Exp $ */ /* @@ -1380,6 +1380,13 @@ sigexit(struct proc *p, int signum) int nosuidcoredump = 1; +struct coredump_iostate { + struct proc *io_proc; + struct vnode *io_vp; + struct ucred *io_cred; + off_t io_offset; +}; + /* * Dump core, into a file named "progname.core", unless the process was * setuid/setgid. @@ -1392,10 +1399,10 @@ coredump(struct proc *p) struct vmspace *vm = p->p_vmspace; struct nameidata nd; struct vattr vattr; + struct coredump_iostate io; int error, error1, len; char name[sizeof("/var/crash/") + MAXCOMLEN + sizeof(".core")]; char *dir = ""; - struct core core; /* * Don't dump if not root and the process has used set user or @@ -1455,6 +1462,31 @@ coredump(struct proc *p) bcopy(p, &p->p_addr->u_kproc.kp_proc, sizeof(struct proc)); fill_eproc(p, &p->p_addr->u_kproc.kp_eproc); + io.io_proc = p; + io.io_vp = vp; + io.io_cred = cred; + io.io_offset = 0; + + error = (*p->p_emul->e_coredump)(p, &io); +out: + VOP_UNLOCK(vp, 0, p); + error1 = vn_close(vp, FWRITE, cred, p); + crfree(cred); + if (error == 0) + error = error1; + return (error); +} + +int +coredump_trad(struct proc *p, void *cookie) +{ + struct coredump_iostate *io = cookie; + struct vmspace *vm = io->io_proc->p_vmspace; + struct vnode *vp = io->io_vp; + struct ucred *cred = io->io_cred; + struct core core; + int error; + core.c_midmag = 0; strlcpy(core.c_name, p->p_comm, sizeof(core.c_name)); core.c_nseg = 0; @@ -1466,26 +1498,41 @@ coredump(struct proc *p) core.c_ssize = (u_long)round_page(ptoa(vm->vm_ssize)); error = cpu_coredump(p, vp, cred, &core); if (error) - goto out; + return (error); /* * uvm_coredump() spits out all appropriate segments. * All that's left to do is to write the core header. */ error = uvm_coredump(p, vp, cred, &core); if (error) - goto out; + return (error); error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&core, (int)core.c_hdrsize, (off_t)0, UIO_SYSSPACE, IO_NODELOCKED|IO_UNIT, cred, NULL, p); -out: - VOP_UNLOCK(vp, 0, p); - error1 = vn_close(vp, FWRITE, cred, p); - crfree(cred); - if (error == 0) - error = error1; return (error); } +int +coredump_write(void *cookie, 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, (void *)data, len, + io->io_offset, segflg, + IO_NODELOCKED|IO_UNIT, io->io_cred, NULL, io->io_proc); + if (error) { + printf("pid %d (%s): %s write of %zu@%p at %lld failed: %d\n", + io->io_proc->p_pid, io->io_proc->p_comm, + segflg == UIO_USERSPACE ? "user" : "system", + len, data, (long long) io->io_offset, error); + return (error); + } + + io->io_offset += len; + return (0); +} + /* * Nonexistent system call-- signal process (may want to handle it). * Flag error in case process won't see signal immediately (blocked or ignored). |