root/usr/src/uts/common/exec/elf/elf_notes.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * Copyright 2012 DEY Storage Systems, Inc.  All rights reserved.
 * Copyright 2018 Joyent, Inc.
 * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 * Copyright 2024 Oxide Computer Company
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/thread.h>
#include <sys/sysmacros.h>
#include <sys/signal.h>
#include <sys/cred.h>
#include <sys/priv.h>
#include <sys/user.h>
#include <sys/file.h>
#include <sys/errno.h>
#include <sys/vnode.h>
#include <sys/mode.h>
#include <sys/vfs.h>
#include <sys/mman.h>
#include <sys/kmem.h>
#include <sys/proc.h>
#include <sys/pathname.h>
#include <sys/cmn_err.h>
#include <sys/systm.h>
#include <sys/elf.h>
#include <sys/vmsystm.h>
#include <sys/debug.h>
#include <sys/procfs.h>
#include <sys/regset.h>
#include <sys/auxv.h>
#include <sys/exec.h>
#include <sys/prsystm.h>
#include <sys/utsname.h>
#include <sys/zone.h>
#include <vm/as.h>
#include <vm/rm.h>
#include <sys/modctl.h>
#include <sys/systeminfo.h>
#include <sys/machelf.h>
#include <sys/sunddi.h>
#include "elf_impl.h"
#if defined(__i386_COMPAT)
#include <sys/sysi86.h>
#endif

void
setup_note_header(Phdr *v, proc_t *p)
{
        int nlwp = p->p_lwpcnt;
        int nzomb = p->p_zombcnt;
        int nfd;
        size_t size;
        prcred_t *pcrp;
        uf_info_t *fip;
        uf_entry_t *ufp;
        int fd;

        fip = P_FINFO(p);
        nfd = 0;
        mutex_enter(&fip->fi_lock);
        for (fd = 0; fd < fip->fi_nfiles; fd++) {
                UF_ENTER(ufp, fip, fd);
                if ((ufp->uf_file != NULL) && (ufp->uf_file->f_count > 0))
                        nfd++;
                UF_EXIT(ufp);
        }
        mutex_exit(&fip->fi_lock);

        v[0].p_type = PT_NOTE;
        v[0].p_flags = PF_R;
        v[0].p_filesz = (sizeof (Note) * (11 + 3 * nlwp + nzomb + nfd))
            + roundup(sizeof (psinfo_t), sizeof (Word))
            + roundup(sizeof (pstatus_t), sizeof (Word))
            + roundup(prgetprivsize(), sizeof (Word))
            + roundup(priv_get_implinfo_size(), sizeof (Word))
            + roundup(strlen(platform) + 1, sizeof (Word))
            + roundup(strlen(p->p_zone->zone_name) + 1, sizeof (Word))
            + roundup(__KERN_NAUXV_IMPL * sizeof (aux_entry_t), sizeof (Word))
            + roundup(sizeof (utsname), sizeof (Word))
            + roundup(sizeof (core_content_t), sizeof (Word))
            + roundup(sizeof (prsecflags_t), sizeof (Word))
            + roundup(sizeof (prcwd_t), sizeof (Word))
            + (nlwp + nzomb) * roundup(sizeof (lwpsinfo_t), sizeof (Word))
            + nlwp * roundup(sizeof (lwpstatus_t), sizeof (Word))
            + nlwp * roundup(sizeof (prlwpname_t), sizeof (Word))
            + nfd * roundup(sizeof (prfdinfo_core_t), sizeof (Word));

        if (curproc->p_agenttp != NULL) {
                v[0].p_filesz += sizeof (Note) +
                    roundup(sizeof (psinfo_t), sizeof (Word));
        }

        size = sizeof (prcred_t) + sizeof (gid_t) * (ngroups_max - 1);
        pcrp = kmem_zalloc(size, KM_SLEEP);
        prgetcred(p, pcrp);
        if (pcrp->pr_ngroups != 0) {
                v[0].p_filesz += sizeof (Note) + roundup(sizeof (prcred_t) +
                    sizeof (gid_t) * (pcrp->pr_ngroups - 1), sizeof (Word));
        } else {
                v[0].p_filesz += sizeof (Note) +
                    roundup(sizeof (prcred_t), sizeof (Word));
        }
        kmem_free(pcrp, size);


#if defined(__i386_COMPAT)
        mutex_enter(&p->p_ldtlock);
        size = prnldt(p) * sizeof (struct ssd);
        mutex_exit(&p->p_ldtlock);
        if (size != 0)
                v[0].p_filesz += sizeof (Note) + roundup(size, sizeof (Word));
#endif  /* __i386_COMPAT */

        if ((size = prhasx(p) ? prgetprxregsize(p) : 0) != 0)
                v[0].p_filesz += nlwp * sizeof (Note)
                    + nlwp * roundup(size, sizeof (Word));

#if defined(__sparc)
        /*
         * Figure out the number and sizes of register windows.
         */
        {
                kthread_t *t = p->p_tlist;
                do {
                        if ((size = prnwindows(ttolwp(t))) != 0) {
                                size = sizeof (gwindows_t) -
                                    (SPARC_MAXREGWINDOW - size) *
                                    sizeof (struct rwindow);
                                v[0].p_filesz += sizeof (Note) +
                                    roundup(size, sizeof (Word));
                        }
                } while ((t = t->t_forw) != p->p_tlist);
        }
        /*
         * Space for the Ancillary State Registers.
         */
        if (p->p_model == DATAMODEL_LP64)
                v[0].p_filesz += nlwp * sizeof (Note)
                    + nlwp * roundup(sizeof (asrset_t), sizeof (Word));
#endif /* __sparc */

        mutex_enter(&p->p_lock);
        if ((p->p_upanicflag & P_UPF_PANICKED) != 0) {
                v[0].p_filesz += sizeof (Note) +
                    roundup(sizeof (prupanic_t), sizeof (Word));
        }
        mutex_exit(&p->p_lock);
}

static void
fill_prcwd(proc_t *p, prcwd_t *cwd, vnode_t *vroot, cred_t *credp)
{
        vnode_t *vn;
        vfs_t *vfsp;
        dev32_t dev;

        vn = p->p_user.u_cdir;
        vfsp = vn->v_vfsp;

        /*
         * Zero out the structure and ensure all the padding is zero as
         * otherwise it may have garbage left over from another note. This also
         * ensures that if we don't have a resource or mount point for some
         * unexpected reason that we end up with an empty string.
         */
        bzero(cwd, sizeof (prcwd_t));

        /*
         * We opt not to check the cached u_cwd as it may or may not exist.
         */
        (void) vnodetopath(vroot, vn, cwd->prcwd_cwd,
            sizeof (cwd->prcwd_cwd), credp);
        (void) cmpldev(&dev, vfsp->vfs_dev);
        cwd->prcwd_fsid = dev;
        (void) strlcpy(cwd->prcwd_fsname, vfssw[vfsp->vfs_fstype].vsw_name,
            sizeof (cwd->prcwd_fsname));
        if (refstr_value(vfsp->vfs_mntpt) != NULL) {
                (void) strlcpy(cwd->prcwd_mntpt, refstr_value(vfsp->vfs_mntpt),
                    sizeof (cwd->prcwd_mntpt));
        }

        if (refstr_value(vfsp->vfs_resource) != NULL) {
                (void) strlcpy(cwd->prcwd_mntspec,
                    refstr_value(vfsp->vfs_resource),
                    sizeof (cwd->prcwd_mntspec));
        }
}

int
write_elfnotes(proc_t *p, int sig, vnode_t *vp, offset_t offset,
    rlim64_t rlimit, cred_t *credp, core_content_t content)
{
        union {
                psinfo_t        psinfo;
                pstatus_t       pstatus;
                lwpsinfo_t      lwpsinfo;
                lwpstatus_t     lwpstatus;
#if defined(__sparc)
                gwindows_t      gwindows;
                asrset_t        asrset;
#endif /* __sparc */
                char            xregs[1];
                aux_entry_t     auxv[__KERN_NAUXV_IMPL];
                prcred_t        pcred;
                prpriv_t        ppriv;
                priv_impl_info_t prinfo;
                struct utsname  uts;
                prsecflags_t    psecflags;
                prupanic_t      upanic;
                prcwd_t         cwd;
        } *bigwad;

        size_t xregsize = prhasx(p) ? prgetprxregsize(p) : 0;
        size_t crsize = sizeof (prcred_t) + sizeof (gid_t) * (ngroups_max - 1);
        size_t psize = prgetprivsize();
        size_t bigsize = MAX(psize, MAX(sizeof (*bigwad),
            MAX(xregsize, crsize)));

        priv_impl_info_t *prii;

        lwpdir_t *ldp;
        lwpent_t *lep;
        kthread_t *t;
        klwp_t *lwp;
        user_t *up;
        int i;
        int nlwp;
        int nzomb;
        int error;
        uchar_t oldsig;
        uf_info_t *fip;
        int fd;
        vnode_t *vroot;

#if defined(__i386_COMPAT)
        struct ssd *ssd;
        size_t ssdsize;
#endif  /* __i386_COMPAT */

        bigsize = MAX(bigsize, priv_get_implinfo_size());

        bigwad = kmem_alloc(bigsize, KM_SLEEP);

        /*
         * The order of the elfnote entries should be same here
         * and in the gcore(1) command.  Synchronization is
         * needed between the kernel and gcore(1).
         */

        /*
         * Get the psinfo, and set the wait status to indicate that a core was
         * dumped.  We have to forge this since p->p_wcode is not set yet.
         */
        mutex_enter(&p->p_lock);
        prgetpsinfo(p, &bigwad->psinfo);
        mutex_exit(&p->p_lock);
        bigwad->psinfo.pr_wstat = wstat(CLD_DUMPED, sig);

        error = elfnote(vp, &offset, NT_PSINFO, sizeof (bigwad->psinfo),
            (caddr_t)&bigwad->psinfo, rlimit, credp);
        if (error)
                goto done;

        /*
         * Modify t_whystop and lwp_cursig so it appears that the current LWP
         * is stopped after faulting on the signal that caused the core dump.
         * As a result, prgetstatus() will record that signal, the saved
         * lwp_siginfo, and its signal handler in the core file status.  We
         * restore lwp_cursig in case a subsequent signal was received while
         * dumping core.
         */
        mutex_enter(&p->p_lock);
        lwp = ttolwp(curthread);

        oldsig = lwp->lwp_cursig;
        lwp->lwp_cursig = (uchar_t)sig;
        curthread->t_whystop = PR_FAULTED;

        prgetstatus(p, &bigwad->pstatus, p->p_zone);
        bigwad->pstatus.pr_lwp.pr_why = 0;

        curthread->t_whystop = 0;
        lwp->lwp_cursig = oldsig;
        mutex_exit(&p->p_lock);

        error = elfnote(vp, &offset, NT_PSTATUS, sizeof (bigwad->pstatus),
            (caddr_t)&bigwad->pstatus, rlimit, credp);
        if (error)
                goto done;

        error = elfnote(vp, &offset, NT_PLATFORM, strlen(platform) + 1,
            platform, rlimit, credp);
        if (error)
                goto done;

        up = PTOU(p);
        for (i = 0; i < __KERN_NAUXV_IMPL; i++) {
                bigwad->auxv[i].a_type = up->u_auxv[i].a_type;
                bigwad->auxv[i].a_un.a_val = up->u_auxv[i].a_un.a_val;
        }
        error = elfnote(vp, &offset, NT_AUXV, sizeof (bigwad->auxv),
            (caddr_t)bigwad->auxv, rlimit, credp);
        if (error)
                goto done;

        bcopy(&utsname, &bigwad->uts, sizeof (struct utsname));
        if (!INGLOBALZONE(p)) {
                bcopy(p->p_zone->zone_nodename, &bigwad->uts.nodename,
                    _SYS_NMLN);
        }
        error = elfnote(vp, &offset, NT_UTSNAME, sizeof (struct utsname),
            (caddr_t)&bigwad->uts, rlimit, credp);
        if (error)
                goto done;

        prgetsecflags(p, &bigwad->psecflags);
        error = elfnote(vp, &offset, NT_SECFLAGS, sizeof (prsecflags_t),
            (caddr_t)&bigwad->psecflags, rlimit, credp);
        if (error)
                goto done;

        bzero(bigwad, crsize);
        prgetcred(p, &bigwad->pcred);

        if (bigwad->pcred.pr_ngroups != 0) {
                crsize = sizeof (prcred_t) +
                    sizeof (gid_t) * (bigwad->pcred.pr_ngroups - 1);
        } else
                crsize = sizeof (prcred_t);

        error = elfnote(vp, &offset, NT_PRCRED, crsize,
            (caddr_t)&bigwad->pcred, rlimit, credp);
        if (error)
                goto done;

        error = elfnote(vp, &offset, NT_CONTENT, sizeof (core_content_t),
            (caddr_t)&content, rlimit, credp);
        if (error)
                goto done;

        prgetpriv(p, &bigwad->ppriv);

        error = elfnote(vp, &offset, NT_PRPRIV, psize,
            (caddr_t)&bigwad->ppriv, rlimit, credp);
        if (error)
                goto done;

        prii = priv_hold_implinfo();
        error = elfnote(vp, &offset, NT_PRPRIVINFO, priv_get_implinfo_size(),
            (caddr_t)prii, rlimit, credp);
        priv_release_implinfo();
        if (error)
                goto done;

        /* zone can't go away as long as process exists */
        error = elfnote(vp, &offset, NT_ZONENAME,
            strlen(p->p_zone->zone_name) + 1, p->p_zone->zone_name,
            rlimit, credp);
        if (error)
                goto done;


        /* open file table */
        vroot = PTOU(p)->u_rdir;
        if (vroot == NULL)
                vroot = rootdir;

        VN_HOLD(vroot);

        fip = P_FINFO(p);

        for (fd = 0; fd < fip->fi_nfiles; fd++) {
                uf_entry_t *ufp;
                vnode_t *fvp;
                struct file *fp;
                vattr_t vattr;
                prfdinfo_core_t fdinfo;

                bzero(&fdinfo, sizeof (fdinfo));

                mutex_enter(&fip->fi_lock);
                UF_ENTER(ufp, fip, fd);
                if (((fp = ufp->uf_file) == NULL) || (fp->f_count < 1)) {
                        UF_EXIT(ufp);
                        mutex_exit(&fip->fi_lock);
                        continue;
                }

                fdinfo.pr_fd = fd;
                fdinfo.pr_fdflags = ufp->uf_flag;
                fdinfo.pr_fileflags = fp->f_flag2;
                fdinfo.pr_fileflags <<= 16;
                fdinfo.pr_fileflags |= fp->f_flag;
                if ((fdinfo.pr_fileflags & (FSEARCH | FEXEC)) == 0)
                        fdinfo.pr_fileflags += FOPEN;
                fdinfo.pr_offset = fp->f_offset;


                fvp = fp->f_vnode;
                VN_HOLD(fvp);
                UF_EXIT(ufp);
                mutex_exit(&fip->fi_lock);

                /*
                 * There are some vnodes that have no corresponding
                 * path.  Its reasonable for this to fail, in which
                 * case the path will remain an empty string.
                 */
                (void) vnodetopath(vroot, fvp, fdinfo.pr_path,
                    sizeof (fdinfo.pr_path), credp);

                if (VOP_GETATTR(fvp, &vattr, 0, credp, NULL) != 0) {
                        /*
                         * Try to write at least a subset of information
                         */
                        fdinfo.pr_major = 0;
                        fdinfo.pr_minor = 0;
                        fdinfo.pr_ino = 0;
                        fdinfo.pr_mode = 0;
                        fdinfo.pr_uid = (uid_t)-1;
                        fdinfo.pr_gid = (gid_t)-1;
                        fdinfo.pr_rmajor = 0;
                        fdinfo.pr_rminor = 0;
                        fdinfo.pr_size = -1;

                        error = elfnote(vp, &offset, NT_FDINFO,
                            sizeof (fdinfo), &fdinfo, rlimit, credp);
                        VN_RELE(fvp);
                        if (error) {
                                VN_RELE(vroot);
                                goto done;
                        }
                        continue;
                }

                if (fvp->v_type == VSOCK)
                        fdinfo.pr_fileflags |= sock_getfasync(fvp);

                VN_RELE(fvp);

                /*
                 * This logic mirrors fstat(), which we cannot use
                 * directly, as it calls copyout().
                 */
                fdinfo.pr_major = getmajor(vattr.va_fsid);
                fdinfo.pr_minor = getminor(vattr.va_fsid);
                fdinfo.pr_ino = (ino64_t)vattr.va_nodeid;
                fdinfo.pr_mode = VTTOIF(vattr.va_type) | vattr.va_mode;
                fdinfo.pr_uid = vattr.va_uid;
                fdinfo.pr_gid = vattr.va_gid;
                fdinfo.pr_rmajor = getmajor(vattr.va_rdev);
                fdinfo.pr_rminor = getminor(vattr.va_rdev);
                fdinfo.pr_size = (off64_t)vattr.va_size;

                error = elfnote(vp, &offset, NT_FDINFO,
                    sizeof (fdinfo), &fdinfo, rlimit, credp);
                if (error) {
                        VN_RELE(vroot);
                        goto done;
                }
        }

        /*
         * Take care of grabbing the cwd while we're here and still have vroot
         * held.
         */
        fill_prcwd(p, &bigwad->cwd, vroot, credp);
        error = elfnote(vp, &offset, NT_CWD, sizeof (bigwad->cwd),
            (caddr_t)&bigwad->cwd, rlimit, credp);
        VN_RELE(vroot);
        if (error)
                goto done;

#if defined(__i386_COMPAT)
        mutex_enter(&p->p_ldtlock);
        ssdsize = prnldt(p) * sizeof (struct ssd);
        if (ssdsize != 0) {
                ssd = kmem_alloc(ssdsize, KM_SLEEP);
                prgetldt(p, ssd);
                error = elfnote(vp, &offset, NT_LDT, ssdsize,
                    (caddr_t)ssd, rlimit, credp);
                kmem_free(ssd, ssdsize);
        }
        mutex_exit(&p->p_ldtlock);
        if (error)
                goto done;
#endif  /* defined(__i386_COMPAT) */

        nlwp = p->p_lwpcnt;
        nzomb = p->p_zombcnt;
        /* for each entry in the lwp directory ... */
        for (ldp = p->p_lwpdir; nlwp + nzomb != 0; ldp++) {
                prlwpname_t name = { 0, };

                if ((lep = ldp->ld_entry) == NULL)      /* empty slot */
                        continue;

                if ((t = lep->le_thread) != NULL) {     /* active lwp */
                        ASSERT(nlwp != 0);
                        nlwp--;
                        lwp = ttolwp(t);
                        mutex_enter(&p->p_lock);
                        prgetlwpsinfo(t, &bigwad->lwpsinfo);
                        if (t->t_name != NULL) {
                                (void) strlcpy(name.pr_lwpname, t->t_name,
                                    sizeof (name.pr_lwpname));
                        }
                        mutex_exit(&p->p_lock);
                } else {                                /* zombie lwp */
                        ASSERT(nzomb != 0);
                        nzomb--;
                        bzero(&bigwad->lwpsinfo, sizeof (bigwad->lwpsinfo));
                        bigwad->lwpsinfo.pr_lwpid = lep->le_lwpid;
                        bigwad->lwpsinfo.pr_state = SZOMB;
                        bigwad->lwpsinfo.pr_sname = 'Z';
                        bigwad->lwpsinfo.pr_start.tv_sec = lep->le_start;
                }

                name.pr_lwpid = bigwad->lwpsinfo.pr_lwpid;

                error = elfnote(vp, &offset, NT_LWPSINFO,
                    sizeof (bigwad->lwpsinfo), (caddr_t)&bigwad->lwpsinfo,
                    rlimit, credp);
                if (error)
                        goto done;

                if (t == NULL)          /* nothing more to do for a zombie */
                        continue;

                mutex_enter(&p->p_lock);
                if (t == curthread) {
                        /*
                         * Modify t_whystop and lwp_cursig so it appears that
                         * the current LWP is stopped after faulting on the
                         * signal that caused the core dump.  As a result,
                         * prgetlwpstatus() will record that signal, the saved
                         * lwp_siginfo, and its signal handler in the core file
                         * status.  We restore lwp_cursig in case a subsequent
                         * signal was received while dumping core.
                         */
                        oldsig = lwp->lwp_cursig;
                        lwp->lwp_cursig = (uchar_t)sig;
                        t->t_whystop = PR_FAULTED;

                        prgetlwpstatus(t, &bigwad->lwpstatus, p->p_zone);
                        bigwad->lwpstatus.pr_why = 0;

                        t->t_whystop = 0;
                        lwp->lwp_cursig = oldsig;
                } else {
                        prgetlwpstatus(t, &bigwad->lwpstatus, p->p_zone);
                }
                mutex_exit(&p->p_lock);
                error = elfnote(vp, &offset, NT_LWPSTATUS,
                    sizeof (bigwad->lwpstatus), (caddr_t)&bigwad->lwpstatus,
                    rlimit, credp);
                if (error)
                        goto done;

                if ((error = elfnote(vp, &offset, NT_LWPNAME, sizeof (name),
                    (caddr_t)&name, rlimit, credp)) != 0)
                        goto done;


#if defined(__sparc)
                /*
                 * Unspilled SPARC register windows.
                 */
                {
                        size_t size = prnwindows(lwp);

                        if (size != 0) {
                                size = sizeof (gwindows_t) -
                                    (SPARC_MAXREGWINDOW - size) *
                                    sizeof (struct rwindow);
                                prgetwindows(lwp, &bigwad->gwindows);
                                error = elfnote(vp, &offset, NT_GWINDOWS,
                                    size, (caddr_t)&bigwad->gwindows,
                                    rlimit, credp);
                                if (error)
                                        goto done;
                        }
                }
                /*
                 * Ancillary State Registers.
                 */
                if (p->p_model == DATAMODEL_LP64) {
                        prgetasregs(lwp, bigwad->asrset);
                        error = elfnote(vp, &offset, NT_ASRS,
                            sizeof (asrset_t), (caddr_t)bigwad->asrset,
                            rlimit, credp);
                        if (error)
                                goto done;
                }
#endif /* __sparc */

                if (xregsize) {
                        prgetprxregs(lwp, (prxregset_t *)bigwad->xregs);
                        error = elfnote(vp, &offset, NT_PRXREG,
                            xregsize, bigwad->xregs, rlimit, credp);
                        if (error)
                                goto done;
                }

                if (t->t_lwp->lwp_spymaster != NULL) {
                        void *psaddr = t->t_lwp->lwp_spymaster;
#ifdef _ELF32_COMPAT
                        /*
                         * On a 64-bit kernel with 32-bit ELF compatibility,
                         * this file is compiled into two different objects:
                         * one is compiled normally, and the other is compiled
                         * with _ELF32_COMPAT set -- and therefore with a
                         * psinfo_t defined to be a psinfo32_t.  However, the
                         * psinfo_t denoting our spymaster is always of the
                         * native type; if we are in the _ELF32_COMPAT case,
                         * we need to explicitly convert it.
                         */
                        if (p->p_model == DATAMODEL_ILP32) {
                                psinfo_kto32(psaddr, &bigwad->psinfo);
                                psaddr = &bigwad->psinfo;
                        }
#endif

                        error = elfnote(vp, &offset, NT_SPYMASTER,
                            sizeof (psinfo_t), psaddr, rlimit, credp);
                        if (error)
                                goto done;
                }
        }
        ASSERT(nlwp == 0);

        /*
         * If a upanic occurred, add a note for it.
         */
        mutex_enter(&p->p_lock);
        if ((p->p_upanicflag & P_UPF_PANICKED) != 0) {
                bzero(&bigwad->upanic, sizeof (prupanic_t));
                bigwad->upanic.pru_version = PRUPANIC_VERSION_1;
                if ((p->p_upanicflag & P_UPF_INVALMSG) != 0) {
                        bigwad->upanic.pru_flags |= PRUPANIC_FLAG_MSG_ERROR;
                }

                if ((p->p_upanicflag & P_UPF_TRUNCMSG) != 0) {
                        bigwad->upanic.pru_flags |= PRUPANIC_FLAG_MSG_TRUNC;
                }

                if ((p->p_upanicflag & P_UPF_HAVEMSG) != 0) {
                        bigwad->upanic.pru_flags |= PRUPANIC_FLAG_MSG_VALID;
                        bcopy(p->p_upanic, bigwad->upanic.pru_data,
                            PRUPANIC_BUFLEN);
                }

                mutex_exit(&p->p_lock);
                error = elfnote(vp, &offset, NT_UPANIC, sizeof (prupanic_t),
                    &bigwad->upanic, rlimit, credp);
                if (error != 0) {
                        goto done;
                }
        } else {
                mutex_exit(&p->p_lock);
        }

done:
        kmem_free(bigwad, bigsize);
        return (error);
}