root/usr/src/lib/libproc/common/Pgcore.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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 2009 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 (c) 2013 by Delphix. All rights reserved.
 * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
 * Copyright 2024 Oxide Computer Company
 */

#define _STRUCTURED_PROC        1

#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <procfs.h>
#include <priv.h>
#include <sys/elf.h>
#include <sys/machelf.h>
#include <sys/sysmacros.h>
#include <sys/systeminfo.h>
#include <sys/proc.h>
#include <sys/utsname.h>
#include <core_shstrtab.h>

#include <sys/old_procfs.h>

#include "Pcontrol.h"
#include "P32ton.h"
#include "proc_fd.h"

typedef struct {
        struct ps_prochandle *P;
        int             pgc_fd;
        off64_t         *pgc_poff;
        off64_t         *pgc_soff;
        off64_t         *pgc_doff;
        core_content_t  pgc_content;
        void            *pgc_chunk;
        size_t          pgc_chunksz;

        shstrtab_t      pgc_shstrtab;
} pgcore_t;

typedef struct {
        int             fd_fd;
        off64_t         *fd_doff;
} fditer_t;

static int
gc_pwrite64(int fd, const void *buf, size_t len, off64_t off)
{
        int err;

        err = pwrite64(fd, buf, len, off);

        if (err < 0)
                return (err);

        /*
         * We will take a page from ZFS's book here and use the otherwise
         * unused EBADE to mean a short write.  Typically this will actually
         * result from ENOSPC or EDQUOT, but we can't be sure.
         */
        if (err < len) {
                errno = EBADE;
                return (-1);
        }

        return (0);
}

int
Pgcore(struct ps_prochandle *P, const char *fname, core_content_t content)
{
        int fd;
        int err;
        int saved_errno;

        if ((fd = creat64(fname, 0666)) < 0)
                return (-1);

        if ((err = Pfgcore(P, fd, content)) != 0) {
                saved_errno = errno;
                (void) close(fd);
                (void) unlink(fname);
                errno = saved_errno;
                return (err);
        }

        return (close(fd));
}

/*
 * Since we don't want to use the old-school procfs interfaces, we use the
 * new-style data structures we already have to construct the old-style
 * data structures. We include these data structures in core files for
 * backward compatability.
 */

static void
mkprstatus(struct ps_prochandle *P, const lwpstatus_t *lsp,
    const lwpsinfo_t *lip, prstatus_t *psp)
{
        bzero(psp, sizeof (*psp));

        if (lsp->pr_flags & PR_STOPPED)
                psp->pr_flags = 0x0001;
        if (lsp->pr_flags & PR_ISTOP)
                psp->pr_flags = 0x0002;
        if (lsp->pr_flags & PR_DSTOP)
                psp->pr_flags = 0x0004;
        if (lsp->pr_flags & PR_ASLEEP)
                psp->pr_flags = 0x0008;
        if (lsp->pr_flags & PR_FORK)
                psp->pr_flags = 0x0010;
        if (lsp->pr_flags & PR_RLC)
                psp->pr_flags = 0x0020;
        /*
         * Note that PR_PTRACE (0x0040) from <sys/old_procfs.h> is never set;
         * PR_PCOMPAT corresponds to PR_PTRACE in the newer <sys/procfs.h>.
         */
        if (lsp->pr_flags & PR_PCINVAL)
                psp->pr_flags = 0x0080;
        if (lsp->pr_flags & PR_ISSYS)
                psp->pr_flags = 0x0100;
        if (lsp->pr_flags & PR_STEP)
                psp->pr_flags = 0x0200;
        if (lsp->pr_flags & PR_KLC)
                psp->pr_flags = 0x0400;
        if (lsp->pr_flags & PR_ASYNC)
                psp->pr_flags = 0x0800;
        if (lsp->pr_flags & PR_PTRACE)
                psp->pr_flags = 0x1000;
        if (lsp->pr_flags & PR_MSACCT)
                psp->pr_flags = 0x2000;
        if (lsp->pr_flags & PR_BPTADJ)
                psp->pr_flags = 0x4000;
        if (lsp->pr_flags & PR_ASLWP)
                psp->pr_flags = 0x8000;

        psp->pr_why = lsp->pr_why;
        psp->pr_what = lsp->pr_what;
        psp->pr_info = lsp->pr_info;
        psp->pr_cursig = lsp->pr_cursig;
        psp->pr_nlwp = P->status.pr_nlwp;
        psp->pr_sigpend = P->status.pr_sigpend;
        psp->pr_sighold = lsp->pr_lwphold;
        psp->pr_altstack = lsp->pr_altstack;
        psp->pr_action = lsp->pr_action;
        psp->pr_pid = P->status.pr_pid;
        psp->pr_ppid = P->status.pr_ppid;
        psp->pr_pgrp = P->status.pr_pgid;
        psp->pr_sid = P->status.pr_sid;
        psp->pr_utime = P->status.pr_utime;
        psp->pr_stime = P->status.pr_stime;
        psp->pr_cutime = P->status.pr_cutime;
        psp->pr_cstime = P->status.pr_cstime;
        (void) strncpy(psp->pr_clname, lsp->pr_clname, sizeof (psp->pr_clname));
        psp->pr_syscall = lsp->pr_syscall;
        psp->pr_nsysarg = lsp->pr_nsysarg;
        bcopy(lsp->pr_sysarg, psp->pr_sysarg, sizeof (psp->pr_sysarg));
        psp->pr_who = lsp->pr_lwpid;
        psp->pr_lwppend = lsp->pr_lwppend;
        psp->pr_oldcontext = (ucontext_t *)lsp->pr_oldcontext;
        psp->pr_brkbase = (caddr_t)P->status.pr_brkbase;
        psp->pr_brksize = P->status.pr_brksize;
        psp->pr_stkbase = (caddr_t)P->status.pr_stkbase;
        psp->pr_stksize = P->status.pr_stksize;
        psp->pr_processor = (short)lip->pr_onpro;
        psp->pr_bind = (short)lip->pr_bindpro;
        psp->pr_instr = lsp->pr_instr;
        bcopy(lsp->pr_reg, psp->pr_reg, sizeof (psp->pr_sysarg));
}

static void
mkprpsinfo(struct ps_prochandle *P, prpsinfo_t *psp)
{
        bzero(psp, sizeof (*psp));
        psp->pr_state = P->psinfo.pr_lwp.pr_state;
        psp->pr_sname = P->psinfo.pr_lwp.pr_sname;
        psp->pr_zomb = (psp->pr_state == SZOMB);
        psp->pr_nice = P->psinfo.pr_lwp.pr_nice;
        psp->pr_flag = P->psinfo.pr_lwp.pr_flag;
        psp->pr_uid = P->psinfo.pr_uid;
        psp->pr_gid = P->psinfo.pr_gid;
        psp->pr_pid = P->psinfo.pr_pid;
        psp->pr_ppid = P->psinfo.pr_ppid;
        psp->pr_pgrp = P->psinfo.pr_pgid;
        psp->pr_sid = P->psinfo.pr_sid;
        psp->pr_addr = (caddr_t)P->psinfo.pr_addr;
        psp->pr_size = P->psinfo.pr_size;
        psp->pr_rssize = P->psinfo.pr_rssize;
        psp->pr_wchan = (caddr_t)P->psinfo.pr_lwp.pr_wchan;
        psp->pr_start = P->psinfo.pr_start;
        psp->pr_time = P->psinfo.pr_time;
        psp->pr_pri = P->psinfo.pr_lwp.pr_pri;
        psp->pr_oldpri = P->psinfo.pr_lwp.pr_oldpri;
        psp->pr_cpu = P->psinfo.pr_lwp.pr_cpu;
        psp->pr_ottydev = cmpdev(P->psinfo.pr_ttydev);
        psp->pr_lttydev = P->psinfo.pr_ttydev;
        (void) strncpy(psp->pr_clname, P->psinfo.pr_lwp.pr_clname,
            sizeof (psp->pr_clname));
        (void) strncpy(psp->pr_fname, P->psinfo.pr_fname,
            sizeof (psp->pr_fname));
        bcopy(&P->psinfo.pr_psargs, &psp->pr_psargs,
            sizeof (psp->pr_psargs));
        psp->pr_syscall = P->psinfo.pr_lwp.pr_syscall;
        psp->pr_ctime = P->psinfo.pr_ctime;
        psp->pr_bysize = psp->pr_size * PAGESIZE;
        psp->pr_byrssize = psp->pr_rssize * PAGESIZE;
        psp->pr_argc = P->psinfo.pr_argc;
        psp->pr_argv = (char **)P->psinfo.pr_argv;
        psp->pr_envp = (char **)P->psinfo.pr_envp;
        psp->pr_wstat = P->psinfo.pr_wstat;
        psp->pr_pctcpu = P->psinfo.pr_pctcpu;
        psp->pr_pctmem = P->psinfo.pr_pctmem;
        psp->pr_euid = P->psinfo.pr_euid;
        psp->pr_egid = P->psinfo.pr_egid;
        psp->pr_aslwpid = 0;
        psp->pr_dmodel = P->psinfo.pr_dmodel;
}

#ifdef _LP64

static void
mkprstatus32(struct ps_prochandle *P, const lwpstatus_t *lsp,
    const lwpsinfo_t *lip, prstatus32_t *psp)
{
        bzero(psp, sizeof (*psp));

        if (lsp->pr_flags & PR_STOPPED)
                psp->pr_flags = 0x0001;
        if (lsp->pr_flags & PR_ISTOP)
                psp->pr_flags = 0x0002;
        if (lsp->pr_flags & PR_DSTOP)
                psp->pr_flags = 0x0004;
        if (lsp->pr_flags & PR_ASLEEP)
                psp->pr_flags = 0x0008;
        if (lsp->pr_flags & PR_FORK)
                psp->pr_flags = 0x0010;
        if (lsp->pr_flags & PR_RLC)
                psp->pr_flags = 0x0020;
        /*
         * Note that PR_PTRACE (0x0040) from <sys/old_procfs.h> is never set;
         * PR_PCOMPAT corresponds to PR_PTRACE in the newer <sys/procfs.h>.
         */
        if (lsp->pr_flags & PR_PCINVAL)
                psp->pr_flags = 0x0080;
        if (lsp->pr_flags & PR_ISSYS)
                psp->pr_flags = 0x0100;
        if (lsp->pr_flags & PR_STEP)
                psp->pr_flags = 0x0200;
        if (lsp->pr_flags & PR_KLC)
                psp->pr_flags = 0x0400;
        if (lsp->pr_flags & PR_ASYNC)
                psp->pr_flags = 0x0800;
        if (lsp->pr_flags & PR_PTRACE)
                psp->pr_flags = 0x1000;
        if (lsp->pr_flags & PR_MSACCT)
                psp->pr_flags = 0x2000;
        if (lsp->pr_flags & PR_BPTADJ)
                psp->pr_flags = 0x4000;
        if (lsp->pr_flags & PR_ASLWP)
                psp->pr_flags = 0x8000;

        psp->pr_why = lsp->pr_why;
        psp->pr_what = lsp->pr_what;
        siginfo_n_to_32(&lsp->pr_info, &psp->pr_info);
        psp->pr_cursig = lsp->pr_cursig;
        psp->pr_nlwp = P->status.pr_nlwp;
        psp->pr_sigpend = P->status.pr_sigpend;
        psp->pr_sighold = lsp->pr_lwphold;
        stack_n_to_32(&lsp->pr_altstack, &psp->pr_altstack);
        sigaction_n_to_32(&lsp->pr_action, &psp->pr_action);
        psp->pr_pid = P->status.pr_pid;
        psp->pr_ppid = P->status.pr_ppid;
        psp->pr_pgrp = P->status.pr_pgid;
        psp->pr_sid = P->status.pr_sid;
        timestruc_n_to_32(&P->status.pr_utime, &psp->pr_utime);
        timestruc_n_to_32(&P->status.pr_stime, &psp->pr_stime);
        timestruc_n_to_32(&P->status.pr_cutime, &psp->pr_cutime);
        timestruc_n_to_32(&P->status.pr_cstime, &psp->pr_cstime);
        (void) strncpy(psp->pr_clname, lsp->pr_clname, sizeof (psp->pr_clname));
        psp->pr_syscall = lsp->pr_syscall;
        psp->pr_nsysarg = lsp->pr_nsysarg;
        bcopy(lsp->pr_sysarg, psp->pr_sysarg, sizeof (psp->pr_sysarg));
        psp->pr_who = lsp->pr_lwpid;
        psp->pr_lwppend = lsp->pr_lwppend;
        psp->pr_oldcontext = (caddr32_t)lsp->pr_oldcontext;
        psp->pr_brkbase = (caddr32_t)P->status.pr_brkbase;
        psp->pr_brksize = P->status.pr_brksize;
        psp->pr_stkbase = (caddr32_t)P->status.pr_stkbase;
        psp->pr_stksize = P->status.pr_stksize;
        psp->pr_processor = (short)lip->pr_onpro;
        psp->pr_bind = (short)lip->pr_bindpro;
        psp->pr_instr = lsp->pr_instr;
        bcopy(lsp->pr_reg, psp->pr_reg, sizeof (psp->pr_sysarg));
}

static void
mkprpsinfo32(struct ps_prochandle *P, prpsinfo32_t *psp)
{
        bzero(psp, sizeof (*psp));
        psp->pr_state = P->psinfo.pr_lwp.pr_state;
        psp->pr_sname = P->psinfo.pr_lwp.pr_sname;
        psp->pr_zomb = (psp->pr_state == SZOMB);
        psp->pr_nice = P->psinfo.pr_lwp.pr_nice;
        psp->pr_flag = P->psinfo.pr_lwp.pr_flag;
        psp->pr_uid = P->psinfo.pr_uid;
        psp->pr_gid = P->psinfo.pr_gid;
        psp->pr_pid = P->psinfo.pr_pid;
        psp->pr_ppid = P->psinfo.pr_ppid;
        psp->pr_pgrp = P->psinfo.pr_pgid;
        psp->pr_sid = P->psinfo.pr_sid;
        psp->pr_addr = (caddr32_t)P->psinfo.pr_addr;
        psp->pr_size = P->psinfo.pr_size;
        psp->pr_rssize = P->psinfo.pr_rssize;
        psp->pr_wchan = (caddr32_t)P->psinfo.pr_lwp.pr_wchan;
        timestruc_n_to_32(&P->psinfo.pr_start, &psp->pr_start);
        timestruc_n_to_32(&P->psinfo.pr_time, &psp->pr_time);
        psp->pr_pri = P->psinfo.pr_lwp.pr_pri;
        psp->pr_oldpri = P->psinfo.pr_lwp.pr_oldpri;
        psp->pr_cpu = P->psinfo.pr_lwp.pr_cpu;
        psp->pr_ottydev = cmpdev(P->psinfo.pr_ttydev);
        psp->pr_lttydev = prcmpldev(P->psinfo.pr_ttydev);
        (void) strncpy(psp->pr_clname, P->psinfo.pr_lwp.pr_clname,
            sizeof (psp->pr_clname));
        (void) strncpy(psp->pr_fname, P->psinfo.pr_fname,
            sizeof (psp->pr_fname));
        bcopy(&P->psinfo.pr_psargs, &psp->pr_psargs,
            sizeof (psp->pr_psargs));
        psp->pr_syscall = P->psinfo.pr_lwp.pr_syscall;
        timestruc_n_to_32(&P->psinfo.pr_ctime, &psp->pr_ctime);
        psp->pr_bysize = psp->pr_size * PAGESIZE;
        psp->pr_byrssize = psp->pr_rssize * PAGESIZE;
        psp->pr_argc = P->psinfo.pr_argc;
        psp->pr_argv = (caddr32_t)P->psinfo.pr_argv;
        psp->pr_envp = (caddr32_t)P->psinfo.pr_envp;
        psp->pr_wstat = P->psinfo.pr_wstat;
        psp->pr_pctcpu = P->psinfo.pr_pctcpu;
        psp->pr_pctmem = P->psinfo.pr_pctmem;
        psp->pr_euid = P->psinfo.pr_euid;
        psp->pr_egid = P->psinfo.pr_egid;
        psp->pr_aslwpid = 0;
        psp->pr_dmodel = P->psinfo.pr_dmodel;
}

#endif  /* _LP64 */

static int
write_note(int fd, uint_t type, const void *desc, size_t descsz, off64_t *offp)
{
        /*
         * Note headers are the same regardless of the data model of the
         * ELF file; we arbitrarily use Elf64_Nhdr here.
         */
        struct {
                Elf64_Nhdr nhdr;
                char name[8];
        } n;

        bzero(&n, sizeof (n));
        bcopy("CORE", n.name, 4);
        n.nhdr.n_type = type;
        n.nhdr.n_namesz = 5;
        n.nhdr.n_descsz = roundup(descsz, 4);

        if (gc_pwrite64(fd, &n, sizeof (n), *offp) != 0)
                return (-1);

        *offp += sizeof (n);

        if (gc_pwrite64(fd, desc, n.nhdr.n_descsz, *offp) != 0)
                return (-1);

        *offp += n.nhdr.n_descsz;

        return (0);
}

static int
old_per_lwp(void *data, const lwpstatus_t *lsp, const lwpsinfo_t *lip)
{
        pgcore_t *pgc = data;
        struct ps_prochandle *P = pgc->P;

        /*
         * Legacy core files don't contain information about zombie LWPs.
         * We use Plwp_iter_all() so that we get the lwpsinfo_t structure
         * more cheaply.
         */
        if (lsp == NULL)
                return (0);

        if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
                prstatus_t prstatus;
                mkprstatus(P, lsp, lip, &prstatus);
                if (write_note(pgc->pgc_fd, NT_PRSTATUS, &prstatus,
                    sizeof (prstatus_t), pgc->pgc_doff) != 0)
                        return (0);
                if (write_note(pgc->pgc_fd, NT_PRFPREG, &lsp->pr_fpreg,
                    sizeof (prfpregset_t), pgc->pgc_doff) != 0)
                        return (1);
#ifdef _LP64
        } else {
                prstatus32_t pr32;
                prfpregset32_t pf32;
                mkprstatus32(P, lsp, lip, &pr32);
                if (write_note(pgc->pgc_fd, NT_PRSTATUS, &pr32,
                    sizeof (prstatus32_t), pgc->pgc_doff) != 0)
                        return (1);
                prfpregset_n_to_32(&lsp->pr_fpreg, &pf32);
                if (write_note(pgc->pgc_fd, NT_PRFPREG, &pf32,
                    sizeof (prfpregset32_t), pgc->pgc_doff) != 0)
                        return (1);
#endif  /* _LP64 */
        }

        return (0);
}

static int
new_per_lwp(void *data, const lwpstatus_t *lsp, const lwpsinfo_t *lip)
{
        pgcore_t *pgc = data;
        struct ps_prochandle *P = pgc->P;
        prlwpname_t name = { 0, "" };
        psinfo_t ps;
        prxregset_t *xregs;
        size_t size;

        /*
         * If lsp is NULL this indicates that this is a zombie LWP in
         * which case we dump only the lwpsinfo_t structure and none of
         * the other ancillary LWP state data.
         */
        if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
                if (write_note(pgc->pgc_fd, NT_LWPSINFO, lip,
                    sizeof (lwpsinfo_t), pgc->pgc_doff) != 0)
                        return (1);
                if (lsp == NULL)
                        return (0);
                if (write_note(pgc->pgc_fd, NT_LWPSTATUS, lsp,
                    sizeof (lwpstatus_t), pgc->pgc_doff) != 0)
                        return (1);
#ifdef _LP64
        } else {
                lwpsinfo32_t li32;
                lwpstatus32_t ls32;
                lwpsinfo_n_to_32(lip, &li32);
                if (write_note(pgc->pgc_fd, NT_LWPSINFO, &li32,
                    sizeof (lwpsinfo32_t), pgc->pgc_doff) != 0)
                        return (1);
                if (lsp == NULL)
                        return (0);
                lwpstatus_n_to_32(lsp, &ls32);
                if (write_note(pgc->pgc_fd, NT_LWPSTATUS, &ls32,
                    sizeof (lwpstatus32_t), pgc->pgc_doff) != 0)
                        return (1);
#endif  /* _LP64 */
        }

        if (Plwp_getxregs(P, lsp->pr_lwpid, &xregs, &size) == 0) {
                if (write_note(pgc->pgc_fd, NT_PRXREG, xregs, size,
                    pgc->pgc_doff) != 0)
                        return (1);

                Plwp_freexregs(P, xregs, size);
        }

        if (Plwp_getname(P, lsp->pr_lwpid, name.pr_lwpname,
            sizeof (name.pr_lwpname)) == 0) {
                name.pr_lwpid = lsp->pr_lwpid;
                if (write_note(pgc->pgc_fd, NT_LWPNAME, &name,
                    sizeof (name), pgc->pgc_doff) != 0)
                        return (1);
        }

        if (!(lsp->pr_flags & PR_AGENT))
                return (0);

        if (Plwp_getspymaster(P, lsp->pr_lwpid, &ps) != 0)
                return (0);

        if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
                if (write_note(pgc->pgc_fd, NT_SPYMASTER, &ps,
                    sizeof (psinfo_t), pgc->pgc_doff) != 0)
                        return (1);
#ifdef _LP64
        } else {
                psinfo32_t ps32;
                psinfo_n_to_32(&ps, &ps32);
                if (write_note(pgc->pgc_fd, NT_SPYMASTER, &ps32,
                    sizeof (psinfo32_t), pgc->pgc_doff) != 0)
                        return (1);
#endif  /* _LP64 */
        }


        return (0);
}

static int
iter_fd(void *data, const prfdinfo_t *fdinfo)
{
        fditer_t *iter = data;
        prfdinfo_core_t core;
        int ret = 0;

        if (proc_fdinfo_to_core(fdinfo, &core) != 0)
                return (1);

        ret = write_note(iter->fd_fd, NT_FDINFO, &core,
            sizeof (core), iter->fd_doff);

        if (ret != 0)
                return (1);
        return (0);
}

/*
 * Look for sections that begin with the string '.debug_'. In particular, this
 * will catch all DWARF related sections and it will catch those that different
 * folks use that are not related to DWARF, but still begin with this prefix
 * (e.g. .debug_gdb_scripts). Notably though, this does not catch something like
 * stabs (though it could). This really is filtering based on the section name,
 * less so intent.
 */
static boolean_t
is_debug_section(file_info_t *fptr, GElf_Shdr *shdr)
{
        if (shdr->sh_name == 0 || shdr->sh_name > fptr->file_shstrsz)
                return (B_FALSE);

        if (strncmp(fptr->file_shstrs + shdr->sh_name, ".debug_",
            strlen(".debug_")) != 0) {
                return (B_FALSE);
        }

        return (B_TRUE);
}

static uint_t
count_debug(file_info_t *fptr)
{
        uint_t count = 0;
        Elf_Scn *scn = NULL;

        if (fptr->file_elf == NULL || fptr->file_shstrsz <= 1) {
                return (0);
        }

        while ((scn = elf_nextscn(fptr->file_elf, scn)) != NULL) {
                GElf_Shdr shdr;

                if (gelf_getshdr(scn, &shdr) == NULL)
                        continue;

                if (is_debug_section(fptr, &shdr))
                        count++;
        }

        return (count);
}

static uint_t
count_sections(pgcore_t *pgc)
{
        struct ps_prochandle *P = pgc->P;
        file_info_t *fptr;
        uint_t nshdrs = 0;

        if (!(pgc->pgc_content & (CC_CONTENT_CTF | CC_CONTENT_SYMTAB |
            CC_CONTENT_DEBUG))) {
                return (0);
        }

        for (fptr = list_head(&P->file_head); fptr != NULL;
            fptr = list_next(&P->file_head, fptr)) {
                int hit_symtab = 0;

                Pbuild_file_symtab(P, fptr);

                if ((pgc->pgc_content & CC_CONTENT_CTF) &&
                    Pbuild_file_ctf(P, fptr) != NULL) {
                        sym_tbl_t *sym;

                        nshdrs++;

                        if (fptr->file_ctf_dyn) {
                                sym = &fptr->file_dynsym;
                        } else {
                                sym = &fptr->file_symtab;
                                hit_symtab = 1;
                        }

                        if (sym->sym_data_pri != NULL && sym->sym_symn != 0 &&
                            sym->sym_strs != NULL)
                                nshdrs += 2;
                }

                if ((pgc->pgc_content & CC_CONTENT_SYMTAB) && !hit_symtab &&
                    fptr->file_symtab.sym_data_pri != NULL &&
                    fptr->file_symtab.sym_symn != 0 &&
                    fptr->file_symtab.sym_strs != NULL) {
                        nshdrs += 2;
                }

                if ((pgc->pgc_content & CC_CONTENT_DEBUG) != 0)
                        nshdrs += count_debug(fptr);
        }

        return (nshdrs == 0 ? 0 : nshdrs + 2);
}

static int
write_shdr(pgcore_t *pgc, const char *name, uint_t type, ulong_t flags,
    uintptr_t addr, ulong_t offset, size_t size, uint_t link, uint_t info,
    uintptr_t addralign, uintptr_t entsize)
{
        if (pgc->P->status.pr_dmodel == PR_MODEL_ILP32) {
                Elf32_Shdr shdr;

                bzero(&shdr, sizeof (shdr));
                if (!shstrtab_ndx(&pgc->pgc_shstrtab, name, &shdr.sh_name)) {
                        return (-1);
                }
                shdr.sh_type = type;
                shdr.sh_flags = flags;
                shdr.sh_addr = (Elf32_Addr)addr;
                shdr.sh_offset = offset;
                shdr.sh_size = size;
                shdr.sh_link = link;
                shdr.sh_info = info;
                shdr.sh_addralign = addralign;
                shdr.sh_entsize = entsize;

                if (gc_pwrite64(pgc->pgc_fd, &shdr, sizeof (shdr),
                    *pgc->pgc_soff) != 0)
                        return (-1);

                *pgc->pgc_soff += sizeof (shdr);
#ifdef _LP64
        } else {
                Elf64_Shdr shdr;

                bzero(&shdr, sizeof (shdr));
                if (!shstrtab_ndx(&pgc->pgc_shstrtab, name, &shdr.sh_name)) {
                        return (-1);
                }
                shdr.sh_type = type;
                shdr.sh_flags = flags;
                shdr.sh_addr = addr;
                shdr.sh_offset = offset;
                shdr.sh_size = size;
                shdr.sh_link = link;
                shdr.sh_info = info;
                shdr.sh_addralign = addralign;
                shdr.sh_entsize = entsize;

                if (gc_pwrite64(pgc->pgc_fd, &shdr, sizeof (shdr),
                    *pgc->pgc_soff) != 0)
                        return (-1);

                *pgc->pgc_soff += sizeof (shdr);
#endif  /* _LP64 */
        }

        return (0);
}

static int
dump_symtab(pgcore_t *pgc, file_info_t *fptr, uint_t index, int dynsym)
{
        sym_tbl_t *sym = dynsym ? &fptr->file_dynsym : &fptr->file_symtab;
        shstrtype_t symname = dynsym ? STR_DYNSYM : STR_SYMTAB;
        shstrtype_t strname = dynsym ? STR_DYNSTR : STR_STRTAB;
        uint_t symtype = dynsym ? SHT_DYNSYM : SHT_SYMTAB;
        size_t size;
        uintptr_t addr = fptr->file_map->map_pmap.pr_vaddr;

        if (sym->sym_data_pri == NULL || sym->sym_symn == 0 ||
            sym->sym_strs == NULL)
                return (0);

        size = sym->sym_hdr_pri.sh_size;
        if (gc_pwrite64(pgc->pgc_fd, sym->sym_data_pri->d_buf, size,
            *pgc->pgc_doff) != 0)
                return (-1);

        if (write_shdr(pgc, shstrtab_data[symname], symtype, 0, addr,
            *pgc->pgc_doff, size, index + 1, sym->sym_hdr_pri.sh_info,
            sym->sym_hdr_pri.sh_addralign, sym->sym_hdr_pri.sh_entsize) != 0)
                return (-1);

        *pgc->pgc_doff += roundup(size, 8);

        size = sym->sym_strhdr.sh_size;
        if (gc_pwrite64(pgc->pgc_fd, sym->sym_strs, size, *pgc->pgc_doff) != 0)
                return (-1);

        if (write_shdr(pgc, shstrtab_data[strname], SHT_STRTAB, SHF_STRINGS,
            addr, *pgc->pgc_doff, size, 0, 0, 1, 0) != 0)
                return (-1);

        *pgc->pgc_doff += roundup(size, 8);

        return (0);
}

static int
dump_debug(pgcore_t *pgc, file_info_t *fptr, uint_t *indexp)
{
        Elf_Scn *scn = NULL;

        if (fptr->file_elf == NULL || fptr->file_shstrsz <= 1) {
                return (0);
        }

        while ((scn = elf_nextscn(fptr->file_elf, scn)) != NULL) {
                GElf_Shdr shdr;
                Elf_Data *data;

                if (gelf_getshdr(scn, &shdr) == NULL)
                        continue;

                if (!is_debug_section(fptr, &shdr))
                        continue;

                if ((data = elf_getdata(scn, NULL)) == NULL) {
                        return (-1);
                }

                if (gc_pwrite64(pgc->pgc_fd, data->d_buf, data->d_size,
                    *pgc->pgc_doff) != 0)
                        return (-1);

                if (write_shdr(pgc, fptr->file_shstrs + shdr.sh_name,
                    shdr.sh_type, shdr.sh_flags,
                    fptr->file_map->map_pmap.pr_vaddr, *pgc->pgc_doff,
                    data->d_size, 0, shdr.sh_info, shdr.sh_addralign,
                    shdr.sh_entsize) != 0) {
                        return (-1);
                }

                *indexp = *indexp + 1;
                *pgc->pgc_doff += roundup(data->d_size, 8);
        }

        return (0);
}

static int
dump_sections(pgcore_t *pgc)
{
        struct ps_prochandle *P = pgc->P;
        file_info_t *fptr;
        uint_t index = 1;

        if (!(pgc->pgc_content & (CC_CONTENT_CTF | CC_CONTENT_SYMTAB |
            CC_CONTENT_DEBUG))) {
                return (0);
        }

        for (fptr = list_head(&P->file_head); fptr != NULL;
            fptr = list_next(&P->file_head, fptr)) {
                int hit_symtab = 0;

                Pbuild_file_symtab(P, fptr);

                if ((pgc->pgc_content & CC_CONTENT_CTF) &&
                    Pbuild_file_ctf(P, fptr) != NULL) {
                        sym_tbl_t *sym;
                        uint_t dynsym;
                        uint_t symindex = 0;

                        /*
                         * Write the symtab out first so we can correctly
                         * set the sh_link field in the CTF section header.
                         * symindex will be 0 if there is no corresponding
                         * symbol table section.
                         */
                        if (fptr->file_ctf_dyn) {
                                sym = &fptr->file_dynsym;
                                dynsym = 1;
                        } else {
                                sym = &fptr->file_symtab;
                                dynsym = 0;
                                hit_symtab = 1;
                        }

                        if (sym->sym_data_pri != NULL && sym->sym_symn != 0 &&
                            sym->sym_strs != NULL) {
                                symindex = index;
                                if (dump_symtab(pgc, fptr, index, dynsym) != 0)
                                        return (-1);
                                index += 2;
                        }

                        /*
                         * Write the CTF data that we've read out of the
                         * file itself into the core file.
                         */
                        if (gc_pwrite64(pgc->pgc_fd, fptr->file_ctf_buf,
                            fptr->file_ctf_size, *pgc->pgc_doff) != 0)
                                return (-1);

                        if (write_shdr(pgc, shstrtab_data[STR_CTF],
                            SHT_PROGBITS, 0, fptr->file_map->map_pmap.pr_vaddr,
                            *pgc->pgc_doff, fptr->file_ctf_size, symindex, 0,
                            4, 0) != 0)
                                return (-1);

                        index++;
                        *pgc->pgc_doff += roundup(fptr->file_ctf_size, 8);
                }

                if ((pgc->pgc_content & CC_CONTENT_SYMTAB) && !hit_symtab &&
                    fptr->file_symtab.sym_data_pri != NULL &&
                    fptr->file_symtab.sym_symn != 0 &&
                    fptr->file_symtab.sym_strs != NULL) {
                        if (dump_symtab(pgc, fptr, index, 0) != 0)
                                return (-1);
                        index += 2;
                }

                if ((pgc->pgc_content & CC_CONTENT_DEBUG) != 0 &&
                    dump_debug(pgc, fptr, &index) != 0) {
                        return (-1);
                }
        }

        return (0);
}

/*ARGSUSED*/
static int
dump_map(void *data, const prmap_t *pmp, const char *name)
{
        pgcore_t *pgc = data;
        struct ps_prochandle *P = pgc->P;
#ifdef _LP64
        Elf64_Phdr phdr;
#else
        Elf32_Phdr phdr;
#endif
        size_t n;

        bzero(&phdr, sizeof (phdr));
        phdr.p_type = PT_LOAD;
        phdr.p_vaddr = pmp->pr_vaddr;
        phdr.p_memsz = pmp->pr_size;
        if (pmp->pr_mflags & MA_READ)
                phdr.p_flags |= PF_R;
        if (pmp->pr_mflags & MA_WRITE)
                phdr.p_flags |= PF_W;
        if (pmp->pr_mflags & MA_EXEC)
                phdr.p_flags |= PF_X;

        if (pmp->pr_vaddr + pmp->pr_size > P->status.pr_stkbase &&
            pmp->pr_vaddr < P->status.pr_stkbase + P->status.pr_stksize) {
                if (!(pgc->pgc_content & CC_CONTENT_STACK))
                        goto exclude;

        } else if ((pmp->pr_mflags & MA_ANON) &&
            pmp->pr_vaddr + pmp->pr_size > P->status.pr_brkbase &&
            pmp->pr_vaddr < P->status.pr_brkbase + P->status.pr_brksize) {
                if (!(pgc->pgc_content & CC_CONTENT_HEAP))
                        goto exclude;

        } else if (pmp->pr_mflags & MA_ISM) {
                if (pmp->pr_mflags & MA_NORESERVE) {
                        if (!(pgc->pgc_content & CC_CONTENT_DISM))
                                goto exclude;
                } else {
                        if (!(pgc->pgc_content & CC_CONTENT_ISM))
                                goto exclude;
                }

        } else if (pmp->pr_mflags & MA_SHM) {
                if (!(pgc->pgc_content & CC_CONTENT_SHM))
                        goto exclude;

        } else if (pmp->pr_mflags & MA_SHARED) {
                if (pmp->pr_mflags & MA_ANON) {
                        if (!(pgc->pgc_content & CC_CONTENT_SHANON))
                                goto exclude;
                } else {
                        if (!(pgc->pgc_content & CC_CONTENT_SHFILE))
                                goto exclude;
                }

        } else if (pmp->pr_mflags & MA_ANON) {
                if (!(pgc->pgc_content & CC_CONTENT_ANON))
                        goto exclude;

        } else if (phdr.p_flags == (PF_R | PF_X)) {
                if (!(pgc->pgc_content & CC_CONTENT_TEXT))
                        goto exclude;

        } else if (phdr.p_flags == PF_R) {
                if (!(pgc->pgc_content & CC_CONTENT_RODATA))
                        goto exclude;

        } else {
                if (!(pgc->pgc_content & CC_CONTENT_DATA))
                        goto exclude;
        }

        n = 0;
        while (n < pmp->pr_size) {
                size_t csz = MIN(pmp->pr_size - n, pgc->pgc_chunksz);
                ssize_t ret;

                /*
                 * If we happen to have a PROT_NONE mapping, don't try to read
                 * from the address space.
                 */
                if ((pmp->pr_mflags & (MA_READ | MA_WRITE | MA_EXEC)) == 0) {
                        bzero(pgc->pgc_chunk, csz);
                        ret = csz;
                } else {
                        ret = Pread(P, pgc->pgc_chunk, csz, pmp->pr_vaddr + n);
                }

                /*
                 * If we can't read out part of the victim's address
                 * space for some reason ignore that failure and try to
                 * emit a partial core file without that mapping's data.
                 * As in the kernel, we mark these failures with the
                 * PF_SUNW_FAILURE flag and store the errno where the
                 * mapping would have been.
                 */
                if (ret != csz || gc_pwrite64(pgc->pgc_fd, pgc->pgc_chunk, csz,
                    *pgc->pgc_doff + n) != 0) {
                        int err = errno;
                        (void) gc_pwrite64(pgc->pgc_fd, &err, sizeof (err),
                            *pgc->pgc_doff);
                        *pgc->pgc_doff += roundup(sizeof (err), 8);

                        phdr.p_flags |= PF_SUNW_FAILURE;
                        (void) ftruncate64(pgc->pgc_fd, *pgc->pgc_doff);
                        goto exclude;
                }

                n += csz;
        }

        phdr.p_offset = *pgc->pgc_doff;
        phdr.p_filesz = pmp->pr_size;
        *pgc->pgc_doff += roundup(phdr.p_filesz, 8);

exclude:
        if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
                if (gc_pwrite64(pgc->pgc_fd, &phdr, sizeof (phdr),
                    *pgc->pgc_poff) != 0)
                        return (1);

                *pgc->pgc_poff += sizeof (phdr);
#ifdef _LP64
        } else {
                Elf32_Phdr phdr32;

                bzero(&phdr32, sizeof (phdr32));
                phdr32.p_type = phdr.p_type;
                phdr32.p_vaddr = (Elf32_Addr)phdr.p_vaddr;
                phdr32.p_memsz = (Elf32_Word)phdr.p_memsz;
                phdr32.p_flags = phdr.p_flags;
                phdr32.p_offset = (Elf32_Off)phdr.p_offset;
                phdr32.p_filesz = (Elf32_Word)phdr.p_filesz;

                if (gc_pwrite64(pgc->pgc_fd, &phdr32, sizeof (phdr32),
                    *pgc->pgc_poff) != 0)
                        return (1);

                *pgc->pgc_poff += sizeof (phdr32);
#endif  /* _LP64 */
        }

        return (0);
}

int
write_shstrtab(struct ps_prochandle *P, pgcore_t *pgc)
{
        off64_t off = *pgc->pgc_doff;
        size_t size = 0;
        shstrtab_t *s = &pgc->pgc_shstrtab;

        if (shstrtab_size(s) == 1)
                return (0);

        /*
         * Preemptively stick the name of the shstrtab in the string table.
         */
        if (!shstrtab_ndx(&pgc->pgc_shstrtab,
            shstrtab_data[STR_SHSTRTAB], NULL)) {
                return (1);
        }
        size = shstrtab_size(s);

        /*
         * Dump all the strings that we used being sure we include the
         * terminating null character.
         */
        for (shstrtab_ent_t *ent = list_head(&s->sst_names); ent != NULL;
            ent = list_next(&s->sst_names, ent)) {
                if (gc_pwrite64(pgc->pgc_fd, ent->sste_name, ent->sste_len,
                    off + ent->sste_offset) != 0) {
                        return (1);
                }
        }

        if (P->status.pr_dmodel == PR_MODEL_ILP32) {
                Elf32_Shdr shdr;

                bzero(&shdr, sizeof (shdr));
                if (!shstrtab_ndx(&pgc->pgc_shstrtab,
                    shstrtab_data[STR_SHSTRTAB], &shdr.sh_name)) {
                        return (1);
                }
                shdr.sh_size = size;
                shdr.sh_offset = *pgc->pgc_doff;
                shdr.sh_addralign = 1;
                shdr.sh_flags = SHF_STRINGS;
                shdr.sh_type = SHT_STRTAB;

                if (gc_pwrite64(pgc->pgc_fd, &shdr, sizeof (shdr),
                    *pgc->pgc_soff) != 0)
                        return (1);

                *pgc->pgc_soff += sizeof (shdr);
#ifdef _LP64
        } else {
                Elf64_Shdr shdr;

                bzero(&shdr, sizeof (shdr));
                if (!shstrtab_ndx(&pgc->pgc_shstrtab,
                    shstrtab_data[STR_SHSTRTAB], &shdr.sh_name)) {
                        return (1);
                }
                shdr.sh_size = size;
                shdr.sh_offset = *pgc->pgc_doff;
                shdr.sh_addralign = 1;
                shdr.sh_flags = SHF_STRINGS;
                shdr.sh_type = SHT_STRTAB;

                if (gc_pwrite64(pgc->pgc_fd, &shdr, sizeof (shdr),
                    *pgc->pgc_soff) != 0)
                        return (1);

                *pgc->pgc_soff += sizeof (shdr);
#endif  /* _LP64 */
        }

        *pgc->pgc_doff += roundup(size, 8);

        return (0);
}

/*
 * Don't explicity stop the process; that's up to the consumer.
 */
int
Pfgcore(struct ps_prochandle *P, int fd, core_content_t content)
{
        char plat[SYS_NMLN];
        char zonename[ZONENAME_MAX];
        int platlen = -1;
        pgcore_t pgc;
        off64_t poff, soff, doff, boff;
        struct utsname uts;
        uint_t nphdrs, nshdrs;

        if (ftruncate64(fd, 0) != 0)
                return (-1);

        if (content == CC_CONTENT_INVALID) {
                errno = EINVAL;
                return (-1);
        }

        /*
         * Cache the mappings and other useful data.
         */
        (void) Prd_agent(P);
        (void) Ppsinfo(P);

        (void) memset(&pgc, 0, sizeof (pgc));
        pgc.P = P;
        pgc.pgc_fd = fd;
        pgc.pgc_poff = &poff;
        pgc.pgc_soff = &soff;
        pgc.pgc_doff = &doff;
        pgc.pgc_content = content;
        pgc.pgc_chunksz = PAGESIZE;
        if ((pgc.pgc_chunk = malloc(pgc.pgc_chunksz)) == NULL)
                return (-1);

        if (!shstrtab_init(&pgc.pgc_shstrtab)) {
                goto err;
        }

        /*
         * There are two PT_NOTE program headers for ancillary data, and
         * one for each mapping.
         */
        nphdrs = 2 + P->map_count;
        nshdrs = count_sections(&pgc);

        (void) Pplatform(P, plat, sizeof (plat));
        platlen = strlen(plat) + 1;
        Preadauxvec(P);
        (void) Puname(P, &uts);
        if (Pzonename(P, zonename, sizeof (zonename)) == NULL)
                zonename[0] = '\0';

        /*
         * The core file contents may required zero section headers, but if we
         * overflow the 16 bits allotted to the program header count in the ELF
         * header, we'll need that program header at index zero.
         */
        if (nshdrs == 0 && nphdrs >= PN_XNUM)
                nshdrs = 1;

        /*
         * Set up the ELF header.
         */
        if (P->status.pr_dmodel == PR_MODEL_ILP32) {
                Elf32_Ehdr ehdr;

                bzero(&ehdr, sizeof (ehdr));
                ehdr.e_ident[EI_MAG0] = ELFMAG0;
                ehdr.e_ident[EI_MAG1] = ELFMAG1;
                ehdr.e_ident[EI_MAG2] = ELFMAG2;
                ehdr.e_ident[EI_MAG3] = ELFMAG3;
                ehdr.e_type = ET_CORE;

                ehdr.e_ident[EI_CLASS] = ELFCLASS32;
#if defined(__sparc)
                ehdr.e_machine = EM_SPARC;
                ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
#elif defined(__i386) || defined(__amd64)
                ehdr.e_machine = EM_386;
                ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
#else
#error "unknown machine type"
#endif
                ehdr.e_ident[EI_VERSION] = EV_CURRENT;

                ehdr.e_version = EV_CURRENT;
                ehdr.e_ehsize = sizeof (ehdr);

                if (nphdrs >= PN_XNUM)
                        ehdr.e_phnum = PN_XNUM;
                else
                        ehdr.e_phnum = (unsigned short)nphdrs;

                ehdr.e_phentsize = sizeof (Elf32_Phdr);
                ehdr.e_phoff = ehdr.e_ehsize;

                if (nshdrs > 0) {
                        if (nshdrs >= SHN_LORESERVE)
                                ehdr.e_shnum = 0;
                        else
                                ehdr.e_shnum = (unsigned short)nshdrs;

                        if (nshdrs - 1 >= SHN_LORESERVE)
                                ehdr.e_shstrndx = SHN_XINDEX;
                        else
                                ehdr.e_shstrndx = (unsigned short)(nshdrs - 1);

                        ehdr.e_shentsize = sizeof (Elf32_Shdr);
                        ehdr.e_shoff = ehdr.e_phoff + ehdr.e_phentsize * nphdrs;
                }

                if (gc_pwrite64(fd, &ehdr, sizeof (ehdr), 0) != 0)
                        goto err;

                poff = ehdr.e_phoff;
                soff = ehdr.e_shoff;
                doff = boff = ehdr.e_ehsize +
                    ehdr.e_phentsize * nphdrs +
                    ehdr.e_shentsize * nshdrs;

#ifdef _LP64
        } else {
                Elf64_Ehdr ehdr;

                bzero(&ehdr, sizeof (ehdr));
                ehdr.e_ident[EI_MAG0] = ELFMAG0;
                ehdr.e_ident[EI_MAG1] = ELFMAG1;
                ehdr.e_ident[EI_MAG2] = ELFMAG2;
                ehdr.e_ident[EI_MAG3] = ELFMAG3;
                ehdr.e_type = ET_CORE;

                ehdr.e_ident[EI_CLASS] = ELFCLASS64;
#if defined(__sparc)
                ehdr.e_machine = EM_SPARCV9;
                ehdr.e_ident[EI_DATA] = ELFDATA2MSB;
#elif defined(__i386) || defined(__amd64)
                ehdr.e_machine = EM_AMD64;
                ehdr.e_ident[EI_DATA] = ELFDATA2LSB;
#else
#error "unknown machine type"
#endif
                ehdr.e_ident[EI_VERSION] = EV_CURRENT;

                ehdr.e_version = EV_CURRENT;
                ehdr.e_ehsize = sizeof (ehdr);

                if (nphdrs >= PN_XNUM)
                        ehdr.e_phnum = PN_XNUM;
                else
                        ehdr.e_phnum = (unsigned short)nphdrs;

                ehdr.e_phentsize = sizeof (Elf64_Phdr);
                ehdr.e_phoff = ehdr.e_ehsize;

                if (nshdrs > 0) {
                        if (nshdrs >= SHN_LORESERVE)
                                ehdr.e_shnum = 0;
                        else
                                ehdr.e_shnum = (unsigned short)nshdrs;

                        if (nshdrs - 1 >= SHN_LORESERVE)
                                ehdr.e_shstrndx = SHN_XINDEX;
                        else
                                ehdr.e_shstrndx = (unsigned short)(nshdrs - 1);

                        ehdr.e_shentsize = sizeof (Elf64_Shdr);
                        ehdr.e_shoff = ehdr.e_phoff + ehdr.e_phentsize * nphdrs;
                }

                if (gc_pwrite64(fd, &ehdr, sizeof (ehdr), 0) != 0)
                        goto err;

                poff = ehdr.e_phoff;
                soff = ehdr.e_shoff;
                doff = boff = ehdr.e_ehsize +
                    ehdr.e_phentsize * nphdrs +
                    ehdr.e_shentsize * nshdrs;

#endif  /* _LP64 */
        }

        /*
         * Write the zero indexed section if it exists.
         */
        if (nshdrs > 0 && write_shdr(&pgc, shstrtab_data[STR_NONE], 0, 0, 0, 0,
            nshdrs >= SHN_LORESERVE ? nshdrs : 0,
            nshdrs - 1 >= SHN_LORESERVE ? nshdrs - 1 : 0,
            nphdrs >= PN_XNUM ? nphdrs : 0, 0, 0) != 0)
                goto err;

        /*
         * Construct the old-style note header and section.
         */

        if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
                prpsinfo_t prpsinfo;

                mkprpsinfo(P, &prpsinfo);
                if (write_note(fd, NT_PRPSINFO, &prpsinfo, sizeof (prpsinfo_t),
                    &doff) != 0) {
                        goto err;
                }
                if (write_note(fd, NT_AUXV, P->auxv,
                    P->nauxv * sizeof (P->auxv[0]), &doff) != 0) {
                        goto err;
                }
#ifdef _LP64
        } else {
                prpsinfo32_t pi32;
                auxv32_t *av32;
                size_t size = sizeof (auxv32_t) * P->nauxv;
                int i;

                mkprpsinfo32(P, &pi32);
                if (write_note(fd, NT_PRPSINFO, &pi32, sizeof (prpsinfo32_t),
                    &doff) != 0) {
                        goto err;
                }

                if ((av32 = malloc(size)) == NULL)
                        goto err;

                for (i = 0; i < P->nauxv; i++) {
                        auxv_n_to_32(&P->auxv[i], &av32[i]);
                }

                if (write_note(fd, NT_AUXV, av32, size, &doff) != 0) {
                        free(av32);
                        goto err;
                }

                free(av32);
#endif  /* _LP64 */
        }

        if (write_note(fd, NT_PLATFORM, plat, platlen, &doff) != 0)
                goto err;

        if (Plwp_iter_all(P, old_per_lwp, &pgc) != 0)
                goto err;

        if (P->status.pr_dmodel == PR_MODEL_ILP32) {
                Elf32_Phdr phdr;

                bzero(&phdr, sizeof (phdr));
                phdr.p_type = PT_NOTE;
                phdr.p_flags = PF_R;
                phdr.p_offset = (Elf32_Off)boff;
                phdr.p_filesz = doff - boff;
                boff = doff;

                if (gc_pwrite64(fd, &phdr, sizeof (phdr), poff) != 0)
                        goto err;
                poff += sizeof (phdr);
#ifdef _LP64
        } else {
                Elf64_Phdr phdr;

                bzero(&phdr, sizeof (phdr));
                phdr.p_type = PT_NOTE;
                phdr.p_flags = PF_R;
                phdr.p_offset = boff;
                phdr.p_filesz = doff - boff;
                boff = doff;

                if (gc_pwrite64(fd, &phdr, sizeof (phdr), poff) != 0)
                        goto err;
                poff += sizeof (phdr);
#endif  /* _LP64 */
        }

        /*
         * Construct the new-style note header and section.
         */

        if (P->status.pr_dmodel == PR_MODEL_NATIVE) {
                if (write_note(fd, NT_PSINFO, &P->psinfo, sizeof (psinfo_t),
                    &doff) != 0) {
                        goto err;
                }
                if (write_note(fd, NT_PSTATUS, &P->status, sizeof (pstatus_t),
                    &doff) != 0) {
                        goto err;
                }
                if (write_note(fd, NT_AUXV, P->auxv,
                    P->nauxv * sizeof (P->auxv[0]), &doff) != 0) {
                        goto err;
                }
#ifdef _LP64
        } else {
                psinfo32_t pi32;
                pstatus32_t ps32;
                auxv32_t *av32;
                size_t size = sizeof (auxv32_t) * P->nauxv;
                int i;

                psinfo_n_to_32(&P->psinfo, &pi32);
                if (write_note(fd, NT_PSINFO, &pi32, sizeof (psinfo32_t),
                    &doff) != 0) {
                        goto err;
                }
                pstatus_n_to_32(&P->status, &ps32);
                if (write_note(fd, NT_PSTATUS, &ps32, sizeof (pstatus32_t),
                    &doff) != 0) {
                        goto err;
                }
                if ((av32 = malloc(size)) == NULL)
                        goto err;

                for (i = 0; i < P->nauxv; i++) {
                        auxv_n_to_32(&P->auxv[i], &av32[i]);
                }

                if (write_note(fd, NT_AUXV, av32, size, &doff) != 0) {
                        free(av32);
                        goto err;
                }

                free(av32);
#endif  /* _LP64 */
        }

        if (write_note(fd, NT_PLATFORM, plat, platlen, &doff) != 0 ||
            write_note(fd, NT_UTSNAME, &uts, sizeof (uts), &doff) != 0 ||
            write_note(fd, NT_CONTENT, &content, sizeof (content), &doff) != 0)
                goto err;

        {
                prcred_t cred, *cp;
                size_t size = sizeof (prcred_t);

                if (Pcred(P, &cred, 0) != 0)
                        goto err;

                if (cred.pr_ngroups > 0)
                        size += sizeof (gid_t) * (cred.pr_ngroups - 1);
                if ((cp = malloc(size)) == NULL)
                        goto err;

                if (Pcred(P, cp, cred.pr_ngroups) != 0 ||
                    write_note(fd, NT_PRCRED, cp, size, &doff) != 0) {
                        free(cp);
                        goto err;
                }

                free(cp);
        }

        {
                prpriv_t *ppriv = NULL;
                const priv_impl_info_t *pinfo;
                size_t pprivsz, pinfosz;

                if (Ppriv(P, &ppriv) == -1)
                        goto err;
                pprivsz = PRIV_PRPRIV_SIZE(ppriv);

                if (write_note(fd, NT_PRPRIV, ppriv, pprivsz, &doff) != 0) {
                        Ppriv_free(P, ppriv);
                        goto err;
                }
                Ppriv_free(P, ppriv);

                if ((pinfo = getprivimplinfo()) == NULL)
                        goto err;
                pinfosz = PRIV_IMPL_INFO_SIZE(pinfo);

                if (write_note(fd, NT_PRPRIVINFO, pinfo, pinfosz, &doff) != 0)
                        goto err;
        }

        if (write_note(fd, NT_ZONENAME, zonename, strlen(zonename) + 1,
            &doff) != 0)
                goto err;

        {
                fditer_t iter;
                iter.fd_fd = fd;
                iter.fd_doff = &doff;

                if (Pfdinfo_iter(P, iter_fd, &iter) != 0)
                        goto err;
        }


        {
                prsecflags_t *psf = NULL;

                if (Psecflags(P, &psf) != 0)
                        goto err;

                if (write_note(fd, NT_SECFLAGS, psf,
                    sizeof (prsecflags_t), &doff) != 0) {
                        Psecflags_free(psf);
                        goto err;
                }

                Psecflags_free(psf);
        }

        {
                prcwd_t *cwd = NULL;

                if (Pcwd(P, &cwd) != 0)
                        goto err;

                if (write_note(fd, NT_CWD, cwd,
                    sizeof (prcwd_t), &doff) != 0) {
                        Pcwd_free(cwd);
                        goto err;
                }

                Pcwd_free(cwd);
        }

#if defined(__i386) || defined(__amd64)
        /* CSTYLED */
        {
                struct ssd *ldtp;
                size_t size;
                int nldt;

                /*
                 * Only dump out non-zero sized LDT notes.
                 */
                if ((nldt = Pldt(P, NULL, 0)) != 0) {
                        size = sizeof (struct ssd) * nldt;
                        if ((ldtp = malloc(size)) == NULL)
                                goto err;

                        if (Pldt(P, ldtp, nldt) == -1 ||
                            write_note(fd, NT_LDT, ldtp, size, &doff) != 0) {
                                free(ldtp);
                                goto err;
                        }

                        free(ldtp);
                }
        }
#endif  /* __i386 || __amd64 */

        if (Plwp_iter_all(P, new_per_lwp, &pgc) != 0)
                goto err;

        if (P->status.pr_dmodel == PR_MODEL_ILP32) {
                Elf32_Phdr phdr;

                bzero(&phdr, sizeof (phdr));
                phdr.p_type = PT_NOTE;
                phdr.p_flags = PF_R;
                phdr.p_offset = (Elf32_Off)boff;
                phdr.p_filesz = doff - boff;
                boff = doff;

                if (gc_pwrite64(fd, &phdr, sizeof (phdr), poff) != 0)
                        goto err;
                poff += sizeof (phdr);
#ifdef _LP64
        } else {
                Elf64_Phdr phdr;

                bzero(&phdr, sizeof (phdr));
                phdr.p_type = PT_NOTE;
                phdr.p_flags = PF_R;
                phdr.p_offset = boff;
                phdr.p_filesz = doff - boff;
                boff = doff;

                if (gc_pwrite64(fd, &phdr, sizeof (phdr), poff) != 0)
                        goto err;
                poff += sizeof (phdr);
#endif  /* _LP64 */
        }

        /*
         * Construct the headers for each mapping and write out its data
         * if the content parameter indicates that it should be present
         * in the core file.
         */
        if (Pmapping_iter(P, dump_map, &pgc) != 0)
                goto err;

        if (dump_sections(&pgc) != 0)
                goto err;

        if (write_shstrtab(P, &pgc) != 0)
                goto err;

        free(pgc.pgc_chunk);
        shstrtab_fini(&pgc.pgc_shstrtab);

        return (0);

err:
        /*
         * Wipe out anything we may have written if there was an error.
         */
        (void) ftruncate64(fd, 0);
        free(pgc.pgc_chunk);
        shstrtab_fini(&pgc.pgc_shstrtab);

        return (-1);
}

static const char *content_str[] = {
        "stack",        /* CC_CONTENT_STACK */
        "heap",         /* CC_CONTENT_HEAP */
        "shfile",       /* CC_CONTENT_SHFILE */
        "shanon",       /* CC_CONTENT_SHANON */
        "text",         /* CC_CONTENT_TEXT */
        "data",         /* CC_CONTENT_DATA */
        "rodata",       /* CC_CONTENT_RODATA */
        "anon",         /* CC_CONTENT_ANON */
        "shm",          /* CC_CONTENT_SHM */
        "ism",          /* CC_CONTENT_ISM */
        "dism",         /* CC_CONTENT_DISM */
        "ctf",          /* CC_CONTENT_CTF */
        "symtab",       /* CC_CONTENT_SYMTAB */
        "debug"         /* CC_CONTENT_DEBUG */
};

static uint_t ncontent_str = sizeof (content_str) / sizeof (content_str[0]);

#define STREQ(a, b, n)  (strlen(b) == (n) && strncmp(a, b, n) == 0)

int
proc_str2content(const char *str, core_content_t *cp)
{
        const char *cur = str;
        int add = 1;
        core_content_t mask, content = 0;

        for (;;) {
                for (cur = str; isalpha(*cur); cur++)
                        continue;

                if (STREQ(str, "default", cur - str)) {
                        mask = CC_CONTENT_DEFAULT;
                } else if (STREQ(str, "all", cur - str)) {
                        mask = CC_CONTENT_ALL;
                } else if (STREQ(str, "none", cur - str)) {
                        mask = 0;
                } else {
                        int i = 0;

                        while (!STREQ(str, content_str[i], cur - str)) {
                                i++;

                                if (i >= ncontent_str)
                                        return (-1);
                        }

                        mask = (core_content_t)1 << i;
                }

                if (add)
                        content |= mask;
                else
                        content &= ~mask;

                switch (*cur) {
                case '\0':
                        *cp = content;
                        return (0);
                case '+':
                        add = 1;
                        break;
                case '-':
                        add = 0;
                        break;
                default:
                        return (-1);
                }

                str = cur + 1;
        }
}

static int
popc(core_content_t x)
{
        int i;

        for (i = 0; x != 0; i++)
                x &= x - 1;

        return (i);
}

int
proc_content2str(core_content_t content, char *buf, size_t size)
{
        int nonecnt, defcnt, allcnt;
        core_content_t mask, bit;
        int first;
        uint_t index;
        size_t n, tot = 0;

        if (content == 0)
                return ((int)strlcpy(buf, "none", size));

        if (content & ~CC_CONTENT_ALL)
                return ((int)strlcpy(buf, "<invalid>", size));

        nonecnt = popc(content);
        defcnt = 1 + popc(content ^ CC_CONTENT_DEFAULT);
        allcnt = 1 + popc(content ^ CC_CONTENT_ALL);

        if (defcnt <= nonecnt && defcnt <= allcnt) {
                mask = content ^ CC_CONTENT_DEFAULT;
                first = 0;
                tot += (n = strlcpy(buf, "default", size));
                if (n > size)
                        n = size;
                buf += n;
                size -= n;
        } else if (allcnt < nonecnt) {
                mask = content ^ CC_CONTENT_ALL;
                first = 0;
                tot += (n = strlcpy(buf, "all", size));
                if (n > size)
                        n = size;
                buf += n;
                size -= n;
        } else {
                mask = content;
                first = 1;
        }

        while (mask != 0) {
                bit = mask ^ (mask & (mask - 1));

                if (!first) {
                        if (size > 1) {
                                *buf = (bit & content) ? '+' : '-';
                                buf++;
                                size--;
                        }

                        tot++;
                }
                index = popc(bit - 1);
                tot += (n = strlcpy(buf, content_str[index], size));
                if (n > size)
                        n = size;
                buf += n;
                size -= n;

                mask ^= bit;
                first = 0;
        }

        return ((int)tot);
}