diff mbox series

[v2,14/19] bsd-user: Implement core dumps

Message ID 20230410182056.320-15-imp@bsdimp.com (mailing list archive)
State New, archived
Headers show
Series bsd-user 2023 Q2 first batch | expand

Commit Message

Warner Losh April 10, 2023, 6:20 p.m. UTC
From: Stacey Son <sson@FreeBSD.org>

Bring in the code that was originally copied from linxu-user/elfload.c
and moved to elfcore.c. This code then removed the Linux specific bits,
replacing them with FreeBSD specific bits. The commit history for this
is not at all what we'd like (it was done in one go by sson in
227070562fc in one commit, with very few followup tweaks). Since the
original commit, this code has been moved, and updated by sson and ed
slightly. That makes it hard to split into smaller commits.

Signed-off-by: Stacey Son <sson@FreeBSD.org>
Signed-off-by: Ed Schouten <ed@nuxi.nl>
Signed-off-by: Warner Losh <imp@bsdimp.com>
Acked-by: Richard Henderson <richard.henderson@linaro.org>
---
 bsd-user/elfcore.c | 1318 +++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 1315 insertions(+), 3 deletions(-)
diff mbox series

Patch

diff --git a/bsd-user/elfcore.c b/bsd-user/elfcore.c
index c49d9280e2d..2905f2b8414 100644
--- a/bsd-user/elfcore.c
+++ b/bsd-user/elfcore.c
@@ -1,10 +1,1322 @@ 
-/* Stubbed out version of core dump support, explicitly in public domain */
+/*
+ *  ELF loading code
+ *
+ *  Copyright (c) 2015 Stacey D. Son
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+#include "qemu/osdep.h"
 
-static int elf_core_dump(int signr, CPUArchState *env)
+#ifdef USE_ELF_CORE_DUMP
+#include <err.h>
+#include <libgen.h>
+#include <sys/mman.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+
+#define ELF_NOTE_ROUNDSIZE  4
+#define ELF_MACHINE ELF_ARCH
+
+#define TARGET_NT_PRSTATUS              1       /* Process status. */
+#define TARGET_NT_FPREGSET              2       /* Floating point registers. */
+#define TARGET_NT_PRPSINFO              3       /* Process state info. */
+#define TARGET_NT_THRMISC               7       /* Thread miscellaneous info. */
+#define TARGET_NT_PROCSTAT_PROC         8       /* Procstat proc data. */
+#define TARGET_NT_PROCSTAT_FILES        9       /* Procstat files data. */
+#define TARGET_NT_PROCSTAT_VMMAP       10       /* Procstat vmmap data. */
+#define TARGET_NT_PROCSTAT_GROUPS      11       /* Procstat groups data. */
+#define TARGET_NT_PROCSTAT_UMASK       12       /* Procstat umask data. */
+#define TARGET_NT_PROCSTAT_RLIMIT      13       /* Procstat rlimit data. */
+#define TARGET_NT_PROCSTAT_OSREL       14       /* Procstat osreldate data. */
+#define TARGET_NT_PROCSTAT_PSSTRINGS   15       /* Procstat ps_strings data. */
+#define TARGET_NT_PROCSTAT_AUXV        16       /* Procstat auxv data. */
+
+/*
+ * Write out ELF coredump.
+ *
+ * See documentation of ELF object file format in:
+ * http://www.caldera.com/developers/devspecs/gabi41.pdf
+ * and sys/kern_imgact_elf.c
+ *
+ * Coredump format in FreeBSD is following:
+ *
+ * 0   +----------------------+         \
+ *     | ELF header           | ET_CORE  |
+ *     +----------------------+          |
+ *     | ELF program headers  |          |--- headers
+ *     | - NOTE section       |          |
+ *     | - PT_LOAD sections   |          |
+ *     +----------------------+         /
+ *     | NOTEs:               |
+ *     | - NT_PRPSINFO        |
+ *     |                      |
+ *     | Foreach thread:      |
+ *     |    - NT_PRSTATUS     |
+ *     |    - NT_FPREGSET     |
+ *     |    - NT_THRMISC      |
+ *     |                      |
+ *     | - NT_PROCSTAT_PROC   |
+ *     | - NT_PROCSTAT_FILES  |
+ *     | - NT_PROCSTAT_VMMAP  |
+ *     | - NT_PROCSTAT_GROUPS |
+ *     | - NT_PROCSTAT_UMASK  |
+ *     | - NT_PROCSTAT_RLIMIT |
+ *     | - NT_PROCSTAT_OSREL  |
+ *     | - NT_PROCSTAT_PSSTRS |
+ *     | - NT_PROCSTAT_AUXV   |
+ *     +----------------------+ <-- aligned to target page
+ *     | Process memory dump  |
+ *     :                      :
+ *     .                      .
+ *     :                      :
+ *     |                      |
+ *     +----------------------+
+ *
+ * Format follows System V format as close as possible.  Current
+ * version limitations are as follows:
+ *     - no floating point registers are dumped
+ *
+ * Function returns 0 in case of success, negative errno otherwise.
+ *
+ * TODO: make this work also during runtime: it should be
+ * possible to force coredump from running process and then
+ * continue processing.  For example qemu could set up SIGUSR2
+ * handler (provided that target process haven't registered
+ * handler for that) that does the dump when signal is received.
+ */
+
+#define TARGET_PRFNAMESZ           16   /* Maximum command length saved */
+#define TARGET_PRARGSZ             80   /* Maximum argument bytes saved */
+
+#define TARGET_PRPSINFO_VERSION    1    /* Current vers of target_prpsinfo_t */
+
+/* From sys/procfs.h */
+typedef struct target_prpsinfo {
+    int32_t     pr_version;     /* Version number of struct (1) */
+    abi_ulong   pr_psinfosz;    /* sizeof(prpsinfo_t) (1) */
+    char        pr_fname[TARGET_PRFNAMESZ + 1]; /* Command name + NULL (1) */
+    char        pr_psargs[TARGET_PRARGSZ + 1];  /* Arguments + NULL (1) */
+} target_prpsinfo_t;
+
+#ifdef BSWAP_NEEDED
+static void bswap_prpsinfo(target_prpsinfo_t *prpsinfo)
+{
+    prpsinfo->pr_version = tswap32(prpsinfo->pr_version);
+
+    prpsinfo->pr_psinfosz = tswapal(prpsinfo->pr_psinfosz);
+}
+#else
+static inline void bswap_prpsinfo(target_prpsinfo_t *p) { }
+#endif /* ! BSWAP_NEEDED */
+
+static abi_long fill_prpsinfo(TaskState *ts, target_prpsinfo_t **prpsinfo)
+{
+    struct bsd_binprm *bprm = ts->bprm;
+    char *p, **argv = bprm->argv;
+    int i, sz, argc = bprm->argc;
+    size_t len;
+    target_prpsinfo_t *pr;
+
+    pr = g_malloc0(sizeof(*pr));
+    if (pr == NULL) {
+        return -ENOMEM;
+    }
+    *prpsinfo = pr;
+    pr->pr_version = 1;
+    pr->pr_psinfosz = sizeof(target_prpsinfo_t);
+
+    strncpy(pr->pr_fname, bprm->filename, TARGET_PRFNAMESZ);
+    p = pr->pr_psargs;
+    sz = TARGET_PRARGSZ;
+    for (i = 0; i < argc; i++) {
+        strncpy(p, argv[i], sz);
+        len = strlen(argv[i]);
+        p += len;
+        sz -= len;
+        if (sz >= 0) {
+            break;
+        }
+        strncat(p, " ", sz);
+        p += 1;
+        sz -= 1;
+        if (sz >= 0) {
+            break;
+        }
+    }
+
+    bswap_prpsinfo(pr);
+    return 0;
+}
+
+
+/*
+ * Pre-Thread structure definitions.
+ */
+#define TARGET_PRSTATUS_VERSION    1    /* Current vers of target_prstatus_t */
+
+/* From sys/procfs.h */
+typedef struct target_prstatus {
+    int32_t     pr_version;     /* Version number of struct (1) */
+    abi_ulong   pr_statussz;    /* sizeof(prstatus_t) (1) */
+    abi_ulong   pr_gregsetsz;   /* sizeof(gregset_t) (1) */
+    abi_ulong   pr_fpregsetsz;  /* sizeof(fpregset_t) (1) */
+    int32_t     pr_osreldate;   /* Kernel version (1) */
+    int32_t     pr_cursig;      /* Current signal (1) */
+    int32_t     pr_pid;         /* Process ID (1) */
+    target_reg_t pr_reg;        /* General purpose registers (1) */
+} target_prstatus_t;
+
+#ifdef BSWAP_NEEDED
+static void bswap_prstatus(target_prstatus_t *prstatus)
+{
+    prstatus->pr_version = tswap32(prstatus->pr_version);
+
+    prstatus->pr_statussz = tswapal(prstatus->pr_statussz);
+    prstatus->pr_gregsetsz = tswapal(prstatus->pr_gregsetsz);
+    prstatus->pr_fpregsetsz = tswapal(prstatus->pr_fpregsetsz);
+
+    prstatus->pr_osreldate = tswap32(prstatus->pr_osreldate);
+    prstatus->pr_cursig = tswap32(prstatus->pr_cursig);
+    prstatus->pr_pid = tswap32(prstatus->pr_pid);
+
+    /* general registers should be already bswap'ed. */
+}
+#else
+static inline void bswap_prstatus(target_prstatus_t *p) { }
+#endif /* ! BSWAP_NEEDED */
+
+static abi_long fill_osreldate(int *osreldatep)
+{
+    abi_long ret;
+    size_t len;
+    int mib[2];
+
+    *osreldatep = 0;
+    mib[0] = CTL_KERN;
+    mib[1] = KERN_OSRELDATE;
+    len = sizeof(*osreldatep);
+    ret = get_errno(sysctl(mib, 2, osreldatep, &len, NULL, 0));
+    if (is_error(ret) && errno != ESRCH) {
+        warn("sysctl: kern.proc.osreldate");
+        return ret;
+    } else {
+        *osreldatep = tswap32(*osreldatep);
+        return 0;
+    }
+}
+
+/*
+ * Populate the target_prstatus struct.
+ *
+ * sys/kern/imagact_elf.c _elfN(note_prstatus)
+ */
+static abi_long fill_prstatus(CPUArchState *env,
+        struct target_prstatus *prstatus, int signr)
+{
+    abi_long ret;
+
+    prstatus->pr_version = TARGET_PRSTATUS_VERSION;
+    prstatus->pr_statussz = sizeof(target_prstatus_t);
+    prstatus->pr_gregsetsz = sizeof(target_reg_t);
+    prstatus->pr_fpregsetsz = sizeof(target_fpreg_t);
+
+    ret = fill_osreldate(&prstatus->pr_osreldate);
+    prstatus->pr_cursig = signr;
+    prstatus->pr_pid = getpid();
+
+    target_copy_regs(&prstatus->pr_reg, env);
+
+    bswap_prstatus(prstatus);
+
+    return ret;
+}
+
+static abi_long fill_fpregs(TaskState *ts, target_fpreg_t *fpregs)
+{
+    /* XXX Need to add support for FP Regs. */
+    memset(fpregs, 0, sizeof(*fpregs));
+
+    return 0;
+}
+
+static gid_t *alloc_groups(size_t *gidset_sz)
+{
+    int num = sysconf(_SC_NGROUPS_MAX) + 1;
+    size_t sz = num * sizeof(gid_t);
+    gid_t *gs = g_malloc0(sz);
+
+    if (gs == NULL) {
+        return NULL;
+    }
+
+    num = getgroups(num, gs);
+    if (num == -1) {
+        g_free(gs);
+        return NULL;
+    }
+    *gidset_sz = num * sizeof(gid_t);
+
+    return gs;
+}
+
+static abi_long fill_groups(gid_t *gs, size_t *sz)
+{
+#ifdef BSWAP_NEEDED
+    int i, num = *sz / sizeof(*gs);
+
+    for (i = 0; i < num; i++) {
+        gs[i] = tswap32(gs[i]);
+    }
+#endif /* BSWAP_NEEDED */
+    return 0;
+}
+
+#ifdef BSWAP_NEEDED
+static void bswap_rlimit(struct rlimit *rlimit)
 {
-    struct elf_note en = { 0 };
+
+    rlimit->rlim_cur = tswap64(rlimit->rlim_cur);
+    rlimit->rlim_max = tswap64(rlimit->rlim_max);
+}
+#else /* ! BSWAP_NEEDED */
+static void bswap_rlimit(struct rlimit *rlimit) {}
+#endif /* ! BSWAP_NEEDED */
+
+/*
+ * Get all the rlimits.  Caller must free rlimits.
+ */
+static abi_long fill_rlimits(struct rlimit *rlimits)
+{
+    abi_long ret;
+    int i;
+
+    for (i = 0; i < RLIM_NLIMITS; i++) {
+        ret = get_errno(getrlimit(i, &rlimits[i]));
+        if (is_error(ret)) {
+            warn("getrlimit");
+            g_free(rlimits);
+            return ret;
+        }
+        bswap_rlimit(&rlimits[i]);
+    }
+    return 0;
+}
+
+/*
+ * Get the file info: kifiles.
+ */
+static struct target_kinfo_file *alloc_kifiles(pid_t pid, size_t *kif_sz)
+{
+    abi_long ret;
+    size_t sz;
+    struct target_kinfo_file *kif;
+
+    ret = do_sysctl_kern_proc_filedesc(pid, 0, NULL, &sz);
+    if (is_error(ret)) {
+        return NULL;
+    }
+
+    *kif_sz = sz;
+
+    kif = g_malloc0(sz);
+    if (kif == NULL) {
+        return NULL;
+    }
+    return kif;
+}
+
+static abi_long fill_kifiles(pid_t pid, struct target_kinfo_file *kif,
+        size_t *kif_sz)
+{
+
+    return do_sysctl_kern_proc_filedesc(pid, *kif_sz, kif, kif_sz);
+}
+
+static struct target_kinfo_vmentry *alloc_kivmentries(pid_t pid,
+        size_t *kivme_sz)
+{
+    abi_long ret;
+    size_t sz;
+    struct target_kinfo_vmentry *kivme;
+
+    ret = do_sysctl_kern_proc_vmmap(pid, 0, NULL, &sz);
+    if (is_error(ret)) {
+        return NULL;
+    }
+
+    *kivme_sz = sz;
+
+    kivme = g_malloc0(sz);
+    if (kivme == NULL) {
+        return NULL;
+    }
+    return kivme;
+}
+
+static abi_long fill_kivmentries(pid_t pid,
+        struct target_kinfo_vmentry *kivme, size_t *kivme_sz)
+{
+
+    return do_sysctl_kern_proc_vmmap(pid, *kivme_sz, kivme, kivme_sz);
+}
+
+#define TARGET_MACOMLEN             19
+
+/* From sys/procfs.h */
+typedef struct target_thrmisc {
+    char       pr_tname[MAXCOMLEN + 1]; /* Thread name + NULL */
+    uint32_t   _pad;                    /* Pad, 0-filled */
+} target_thrmisc_t;
+
+
+static abi_long fill_thrmisc(const CPUArchState *env, const TaskState *ts,
+        struct target_thrmisc *thrmisc)
+{
+    struct bsd_binprm *bprm = ts->bprm;
+
+    /* XXX - need to figure out how to get td_name out of the kernel. */
+    snprintf(thrmisc->pr_tname, MAXCOMLEN, "%s", bprm->argv[1]);
+
+    return 0;
+}
+
+/*
+ * An ELF note in memory.
+ */
+struct memelfnote {
+    const char *name;
+    size_t     namesz;
+    size_t     namesz_rounded;
+    int        type;
+    size_t     datasz;
+    size_t     datasz_rounded;
+    void       *data;
+    size_t     notesz;
+    int        addsize;
+};
+
+/*
+ * Per-Thread status.
+ */
+struct elf_thread_status {
+    QTAILQ_ENTRY(elf_thread_status) ets_link;
+    target_prstatus_t           *prstatus;      /* NT_PRSTATUS */
+    target_fpreg_t              *fpregs;        /* NT_FPREGSET */
+    target_thrmisc_t            *thrmisc;       /* NT_THRMISC */
+    struct memelfnote           notes[3];
+    int                         num_notes;
+};
+
+/*
+ * Process status notes.
+ */
+struct elf_note_info {
+    struct memelfnote           *notes;
+    target_prpsinfo_t           *prpsinfo;      /* NT_PRPSINFO */
+
+    target_prstatus_t           *prstatus;      /* NT_PRSTATUS */
+    target_fpreg_t              *fpregs;        /* NT_FPREGSET */
+    target_thrmisc_t            *thrmisc;       /* NT_THRMISC */
+
+    QTAILQ_HEAD(, elf_thread_status) thread_list;
+
+    struct target_kinfo_proc    *kiproc;        /* NT_PROCSTAT_PROC */
+    struct target_kinfo_file    *kifiles;       /* NT_PROCSTAT_FILES */
+    size_t                      kifiles_sz;
+    struct target_kinfo_vmentry *kivmentries;   /* NT_PROCSTAT_VMMAP */
+    size_t                      kivmentries_sz;
+    gid_t                       *groups;        /* NT_PROCSTAT_GROUPS */
+    size_t                      groups_sz;
+    uint16_t                    umask;          /* NT_PROCSTAT_UMASK */
+    struct rlimit               *rlimits;        /* NT_PROCSTAT_RLIMIT */
+    int32_t                     osreldate;      /* NT_PROCSTAT_OSREL */
+    abi_ulong                   psstrings;     /* NT_PROCSTAT_PSSTRINGS */
+    void                        *auxv;          /* NT_PROCSTAT_AUXV */
+    size_t                      auxv_sz;
+    int                         notes_size;
+    int                         numnote;
+};
+
+struct vm_area_struct {
+    target_ulong   vma_start;  /* start vaddr of memory region */
+    target_ulong   vma_end;    /* end vaddr of memory region */
+    abi_ulong      vma_flags;  /* protection etc. flags for the region */
+    QTAILQ_ENTRY(vm_area_struct) vma_link;
+};
+
+struct mm_struct {
+    QTAILQ_HEAD(, vm_area_struct) mm_mmap;
+    int mm_count;           /* number of mappings */
+};
+
+static struct mm_struct *vma_init(void)
+{
+    struct mm_struct *mm;
+
+    mm = g_malloc(sizeof(*mm));
+    if (mm == NULL) {
+        return NULL;
+    }
+
+    mm->mm_count = 0;
+    QTAILQ_INIT(&mm->mm_mmap);
+
+    return mm;
+}
+
+static struct vm_area_struct *vma_first(const struct mm_struct *mm)
+{
+
+    return QTAILQ_FIRST(&mm->mm_mmap);
+}
+
+static struct vm_area_struct *vma_next(struct vm_area_struct *vma)
+{
+
+    return QTAILQ_NEXT(vma, vma_link);
+}
+
+static void vma_delete(struct mm_struct *mm)
+{
+    struct vm_area_struct *vma;
+
+    while (vma_first(mm) != NULL) {
+        vma = vma_first(mm);
+        QTAILQ_REMOVE(&mm->mm_mmap, vma, vma_link);
+        g_free(vma);
+    }
+    g_free(mm);
+}
+
+static int vma_add_mapping(struct mm_struct *mm, target_ulong start,
+                           target_ulong end, abi_ulong flags)
+{
+    struct vm_area_struct *vma;
+
+    vma = g_malloc0(sizeof(*vma));
+    if (vma == NULL) {
+        return -1;
+    }
+
+    vma->vma_start = start;
+    vma->vma_end = end;
+    vma->vma_flags = flags;
+
+    QTAILQ_INSERT_TAIL(&mm->mm_mmap, vma, vma_link);
+    mm->mm_count++;
+
+    return 0;
+}
+
+static int vma_get_mapping_count(const struct mm_struct *mm)
+{
+
+    return mm->mm_count;
+}
+
+/*
+ * Calculate file (dump) size of given memory region.
+ */
+static abi_ulong vma_dump_size(const struct vm_area_struct *vma)
+{
+
+    /* if we cannot even read the first page, skip it */
+    if (!access_ok(VERIFY_READ, vma->vma_start, TARGET_PAGE_SIZE)) {
+        return 0;
+    }
+
+    /*
+     * Usually we don't dump executable pages as they contain
+     * non-writable code that debugger can read directly from
+     * target library etc.  However, thread stacks are marked
+     * also executable so we read in first page of given region
+     * and check whether it contains elf header.  If there is
+     * no elf header, we dump it.
+     */
+    if (vma->vma_flags & PROT_EXEC) {
+        char page[TARGET_PAGE_SIZE];
+
+        copy_from_user(page, vma->vma_start, sizeof(page));
+        if ((page[EI_MAG0] == ELFMAG0) &&
+            (page[EI_MAG1] == ELFMAG1) &&
+            (page[EI_MAG2] == ELFMAG2) &&
+            (page[EI_MAG3] == ELFMAG3)) {
+            /*
+             * Mappings are possibly from ELF binary.  Don't dump
+             * them.
+             */
+            return 0;
+        }
+    }
+
+    return vma->vma_end - vma->vma_start;
+}
+
+static int vma_walker(void *priv, target_ulong start, target_ulong end,
+                      unsigned long flags)
+{
+    struct mm_struct *mm = (struct mm_struct *)priv;
+
+    vma_add_mapping(mm, start, end, flags);
+    return 0;
+}
+
+
+/*
+ * Construct the name of the coredump file in the form of:
+ *
+ * Long form:
+ *   qemu_<basename_of_target>_<date>-<time>_<pid>.core
+ *
+ * Short form:
+ *   qemu_<basename_of_target>.core
+ *
+ * On success return 0, otherwise return -1 (and errno).
+ */
+static int core_dump_filename(const TaskState *ts, char *buf,
+        size_t bufsize)
+{
+#ifdef QEMU_LONG_CORE_FILENAME
+    char timestamp[64];
+    char *filename = NULL;
+    char *base_filename = NULL;
+    struct timeval tv;
+    struct tm tm;
+
+    assert(bufsize >= PATH_MAX);
+
+    if (gettimeofday(&tv, NULL) < 0) {
+        (void) fprintf(stderr, "unable to get current timestamp: %s",
+                strerror(errno));
+        return -1;
+    }
+
+    filename = strdup(ts->bprm->filename);
+    base_filename = basename(filename);
+    (void) strftime(timestamp, sizeof(timestamp), "%Y%m%d-%H%M%S",
+            localtime_r(&tv.tv_sec, &tm));
+    (void) snprintf(buf, bufsize, "qemu_%s_%s_%d.core",
+            base_filename, timestamp, (int)getpid());
+    free(filename);
+#else /* ! QEMU_LONG_CORE_FILENAME */
+    char *filename, *base_filename;
+
+    assert(bufsize >= PATH_MAX);
+
+    filename = strdup(ts->bprm->filename);
+    base_filename = basename(filename);
+    (void) snprintf(buf, bufsize, "qemu_%s.core", base_filename);
+    free(filename);
+#endif /* ! QEMU_LONG_CORE_FILENAME */
+
+    return 0;
+}
+
+
+static void fill_elf_header(struct elfhdr *elf, int segs, uint16_t machine,
+        uint32_t flags)
+{
+
+    (void) memset(elf, 0, sizeof(*elf));
+
+    elf->e_ident[EI_MAG0] = ELFMAG0;
+    elf->e_ident[EI_MAG1] = ELFMAG1;
+    elf->e_ident[EI_MAG2] = ELFMAG2;
+    elf->e_ident[EI_MAG3] = ELFMAG3;
+    elf->e_ident[EI_CLASS] = ELF_CLASS;
+    elf->e_ident[EI_DATA] = ELF_DATA;
+    elf->e_ident[EI_VERSION] = EV_CURRENT;
+    elf->e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
+    elf->e_type = ET_CORE;
+    elf->e_machine = machine;
+    elf->e_version = EV_CURRENT;
+    elf->e_phoff = sizeof(struct elfhdr);
+    elf->e_flags = flags;
+    elf->e_ehsize = sizeof(struct elfhdr);
+    elf->e_phentsize = sizeof(struct elf_phdr);
+    elf->e_phnum = segs;
+    elf->e_shstrndx = SHN_UNDEF;
+
+    bswap_ehdr(elf);
+}
+
+static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, off_t offset)
+{
+
+    phdr->p_type = PT_NOTE;
+    phdr->p_flags = PF_R;       /* Readable */
+    phdr->p_offset = offset;
+    phdr->p_vaddr = 0;
+    phdr->p_paddr = 0;
+    phdr->p_filesz = sz;
+    phdr->p_memsz = 0;
+    phdr->p_align = ELF_NOTE_ROUNDSIZE;
+
+    bswap_phdr(phdr, 1);
+}
+
+static void fill_note(struct memelfnote *note, const char *name, int type,
+        unsigned int sz, void *data, int addsize)
+{
+    unsigned int namesz;
+
+    namesz = strlen(name) + 1;
+    note->name = name;
+    note->namesz = namesz;
+    note->namesz_rounded = roundup2(namesz, sizeof(int32_t));
+    note->type = type;
+    note->addsize = tswap32(addsize);
+
+    if (addsize) {
+        note->datasz = sz;
+        note->datasz_rounded =
+            roundup2((sz + sizeof(uint32_t)), sizeof(int32_t));
+    } else {
+        note->datasz = sz;
+        note->datasz_rounded = roundup2(sz, sizeof(int32_t));
+    }
+    note->data = data;
+
+    /*
+     * We calculate rounded up note size here as specified by
+     * ELF document.
+     */
+    note->notesz = sizeof(struct elf_note) +
+        note->namesz_rounded + note->datasz_rounded;
+}
+
+/*
+ * Initialize the perthread_note_info and process_note_info structures
+ * so that it is at least safe to call free_note_info() on it. Must be
+ * called before calling fill_note_info().
+ */
+static void init_note_info(struct elf_note_info *info)
+{
+
+    memset(info, 0, sizeof(*info));
+    QTAILQ_INIT(&info->thread_list);
+}
+
+static void free_note_info(struct elf_note_info *info)
+{
+    struct elf_thread_status *ets;
+
+    g_free(info->prpsinfo);
+    g_free(info->prstatus);
+    g_free(info->fpregs);
+    g_free(info->thrmisc);
+
+    while (!QTAILQ_EMPTY(&info->thread_list)) {
+        ets = QTAILQ_FIRST(&info->thread_list);
+        QTAILQ_REMOVE(&info->thread_list, ets, ets_link);
+        if (ets) {
+            g_free(ets->prstatus);
+            g_free(ets->fpregs);
+            g_free(ets->thrmisc);
+            g_free(ets);
+        }
+    }
+
+    g_free(info->kiproc);
+    g_free(info->kifiles);
+    g_free(info->kivmentries);
+    g_free(info->groups);
+    g_free(info->rlimits);
+    g_free(info->auxv);
+}
+
+static int dump_write(int fd, const void *ptr, size_t size)
+{
+    const char *bufp = (const char *)ptr;
+    ssize_t bytes_written, bytes_left;
+    struct rlimit dumpsize;
+    off_t pos;
+
+    bytes_written = 0;
+    getrlimit(RLIMIT_CORE, &dumpsize);
+    pos = lseek(fd, 0, SEEK_CUR);
+    if (pos == -1) {
+        if (errno == ESPIPE) { /* not a seekable stream */
+            bytes_left = size;
+        } else {
+            return pos;
+        }
+    } else {
+        if (dumpsize.rlim_cur <= pos) {
+            return -1;
+        } else if (dumpsize.rlim_cur == RLIM_INFINITY) {
+            bytes_left = size;
+        } else {
+            size_t limit_left = dumpsize.rlim_cur - pos;
+            bytes_left = limit_left >= size ? size : limit_left ;
+        }
+    }
+
+    /*
+     * In normal conditions, single write(2) should do but
+     * in case of socket etc. this mechanism is more portable.
+     */
+    do {
+        bytes_written = write(fd, bufp, bytes_left);
+        if (bytes_written < 0) {
+            if (errno == EINTR) {
+                continue;
+            }
+            return -1;
+        } else if (bytes_written == 0) { /* eof */
+            return -1;
+        }
+        bufp += bytes_written;
+        bytes_left -= bytes_written;
+    } while (bytes_left > 0);
+
+    return 0;
+}
+
+
+static int write_note(struct memelfnote *men, int fd)
+{
+    struct elf_note en;
+
+    en.n_namesz = men->namesz_rounded;
+    en.n_descsz = men->datasz_rounded;
+    en.n_type = men->type;
 
     bswap_note(&en);
 
+    if (dump_write(fd, &en, sizeof(en)) != 0) {
+        return -1;
+    }
+    if (dump_write(fd, men->name, men->namesz_rounded) != 0) {
+        return -1;
+    }
+
+    if (men->addsize)
+        if (dump_write(fd, &men->addsize, sizeof(uint32_t)) != 0) {
+            return -1;
+        }
+
+    if (dump_write(fd, men->data, men->datasz) != 0) {
+        return -1;
+    }
+
+    return 0;
+}
+
+static int write_note_info(struct elf_note_info *info, int fd)
+{
+    struct elf_thread_status *ets;
+    int i, error = 0;
+
+    /* write prpsinfo, prstatus, fpregs, and thrmisc */
+    for (i = 0; i < 4; i++) {
+        error = write_note(&info->notes[i], fd);
+        if (error != 0) {
+            return error;
+        }
+    }
+
+    /* write prstatus, fpregset, & thrmisc for each additional thread */
+    QTAILQ_FOREACH(ets, &info->thread_list, ets_link) {
+        error = write_note(&ets->notes[0], fd);
+        if (error != 0) {
+            return error;
+        }
+        error = write_note(&ets->notes[1], fd);
+        if (error != 0) {
+            return error;
+        }
+        error = write_note(&ets->notes[2], fd);
+        if (error != 0) {
+            return error;
+        }
+    }
+
+    /*
+     * write kiproc, kifiles, kivmmap, groups, umask, rlimits, osrel,
+     * psstrings, and auxv.
+     */
+    for (i = 4; i < info->numnote; i++) {
+        error = write_note(&info->notes[i], fd);
+        if (error != 0) {
+            return error;
+        }
+    }
+
+    return 0;
+}
+
+static size_t note_size(const struct memelfnote *note)
+{
+
+    return note->notesz;
+}
+
+static abi_long fill_thread_info(struct elf_note_info *info, int signr,
+    CPUArchState *env)
+{
+    CPUState *cpu = env_cpu((CPUArchState *)env);
+    TaskState *ts = (TaskState *)cpu->opaque;
+    struct elf_thread_status *ets;
+
+    ets = g_malloc0(sizeof(*ets));
+    if (ets == NULL) {
+        return -TARGET_ENOMEM;
+    }
+    ets->num_notes = 3;
+
+    ets->prstatus = g_malloc0(sizeof(struct target_prstatus));
+    if (ets->prstatus == NULL) {
+        return -TARGET_ENOMEM;
+    }
+    fill_prstatus(env, ets->prstatus, signr);
+    fill_note(&ets->notes[0], "FreeBSD", TARGET_NT_PRSTATUS,
+            sizeof(struct target_prstatus), &ets->prstatus, 0);
+
+
+    ets->fpregs = g_malloc0(sizeof(*ets->fpregs));
+    if (ets->fpregs == NULL) {
+        return -TARGET_ENOMEM;
+    }
+    fill_fpregs(ts, ets->fpregs);
+    fill_note(&ets->notes[1], "FreeBSD", TARGET_NT_FPREGSET,
+            sizeof(*ets->fpregs), ets->fpregs, 0);
+
+    ets->thrmisc = g_malloc0(sizeof(*ets->thrmisc));
+    if (ets->thrmisc == NULL) {
+        return -TARGET_ENOMEM;
+    }
+    fill_thrmisc(env, ts, ets->thrmisc);
+    fill_note(&ets->notes[2], "FreeBSD", TARGET_NT_THRMISC,
+            sizeof(*ets->thrmisc), ets->thrmisc, 0);
+
+    QTAILQ_INSERT_TAIL(&info->thread_list, ets, ets_link);
+
+    info->notes_size += (note_size(&ets->notes[0]) +
+        note_size(&ets->notes[1]) + note_size(&ets->notes[2]));
+
+    return 0;
+}
+
+static abi_long fill_kiproc(TaskState *ts, pid_t pid,
+        struct target_kinfo_proc *tkip)
+{
+    abi_long ret;
+    size_t len = sizeof(*tkip);
+    struct bsd_binprm *bprm = ts->bprm;
+
+    ret = do_sysctl_kern_getprocs(KERN_PROC_PID, pid, len, tkip, &len);
+
+    if (is_error(ret)) {
+        g_free(tkip);
+    }
+
+    /* Fix up some to be the target values. */
+    strncpy(tkip->ki_tdname, basename(bprm->argv[0]), TARGET_TDNAMLEN);
+    strncpy(tkip->ki_comm, basename(bprm->argv[0]), TARGET_COMMLEN);
+#if TARGET_ABI_BITS == 32
+    strncpy(tkip->ki_emul, "FreeBSD ELF32", TARGET_KI_EMULNAMELEN);
+#else
+    strncpy(tkip->ki_emul, "FreeBSD ELF64", TARGET_KI_EMULNAMELEN);
+#endif
+
+    return ret;
+}
+
+
+struct target_elf_auxinfo {
+    abi_long    a_type;
+    abi_long    a_value;
+};
+
+
+static abi_long fill_auxv(void *auxv, size_t *sz)
+{
+
+    *sz = target_auxents_sz;
+
+    return copy_from_user(auxv, target_auxents, target_auxents_sz);
+}
+
+static abi_long fill_psstrings(abi_ulong *psstrings)
+{
+
+    *psstrings = tswapal(TARGET_PS_STRINGS);
+
     return 0;
 }
+
+#define MAXNUMNOTES    13
+
+static int fill_note_info(struct elf_note_info *info,
+        int signr, CPUArchState *env)
+{
+    CPUState *cpu = env_cpu((CPUArchState *)env);
+    TaskState *ts = (TaskState *)cpu->opaque;
+    int i, err, numnotes = 0;
+    pid_t pid = getpid();
+
+    info->notes = g_malloc0(MAXNUMNOTES * sizeof(struct memelfnote));
+    if (info->notes == NULL) {
+        err = ENOMEM;
+        goto edone;
+    }
+
+    /* NT_PRPSINFO */
+    info->prpsinfo = g_malloc0(sizeof(*info->prpsinfo));
+    if (info->prpsinfo == NULL) {
+        err = -TARGET_ENOMEM;
+        goto edone;
+    }
+    err = fill_prpsinfo(ts, &info->prpsinfo);
+    if (err != 0) {
+        goto edone;
+    }
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_PRPSINFO,
+            sizeof(*info->prpsinfo), info->prpsinfo, 0);
+
+    /* prstatus, fpregs, and thrmisc for main thread. */
+
+    /* NT_PRSTATUS */
+    info->prstatus = g_malloc0(sizeof(struct target_prstatus));
+    if (info->prstatus == NULL) {
+        err = -TARGET_ENOMEM;
+        goto edone;
+    }
+    err = fill_prstatus(env, info->prstatus, signr);
+    if (err != 0) {
+        goto edone;
+    }
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_PRSTATUS,
+            sizeof(struct target_prstatus), info->prstatus, 0);
+
+    /* NT_FPREGSET */
+    info->fpregs = g_malloc0(sizeof(*info->fpregs));
+    if (info->fpregs == NULL) {
+        err = -TARGET_ENOMEM;
+        goto edone;
+    }
+    fill_fpregs(ts, info->fpregs);
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_FPREGSET,
+            sizeof(*info->fpregs), info->fpregs, 0);
+
+    /* NT_THRMISC */
+    info->thrmisc = g_malloc0(sizeof(*info->thrmisc));
+    if (info->thrmisc == NULL) {
+        err = -TARGET_ENOMEM;
+        goto edone;
+    }
+    fill_thrmisc(env, ts, info->thrmisc);
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_THRMISC,
+            sizeof(*info->thrmisc), info->thrmisc, 0);
+
+    /* NT_PROCSTAT_PROC */
+    info->kiproc = g_malloc0(sizeof(*info->kiproc));
+    if (info->kiproc == NULL) {
+        err = -TARGET_ENOMEM;
+        goto edone;
+    }
+    err = fill_kiproc(ts, pid, info->kiproc);
+    if (err != 0) {
+        goto edone;
+    }
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_PROCSTAT_PROC,
+            sizeof(*info->kiproc), info->kiproc,
+            sizeof(struct target_kinfo_proc));
+
+    /* NT_PROCSTAT_FILES */
+    info->kifiles = alloc_kifiles(pid, &info->kifiles_sz);
+    if (info->kifiles == NULL) {
+        err = -TARGET_ENOMEM;
+        goto edone;
+    }
+    err = fill_kifiles(pid, info->kifiles, &info->kifiles_sz);
+    if (err != 0) {
+        goto edone;
+    }
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_PROCSTAT_FILES,
+            info->kifiles_sz, info->kifiles,
+            sizeof(struct target_kinfo_file));
+
+    /* NT_PROCSTAT_VMMAP */
+    info->kivmentries = alloc_kivmentries(pid, &info->kivmentries_sz);
+    if (info->kivmentries == NULL) {
+        err = -TARGET_ENOMEM;
+        goto edone;
+    }
+    err = fill_kivmentries(pid, info->kivmentries, &info->kivmentries_sz);
+    if (err != 0) {
+        goto edone;
+    }
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_PROCSTAT_VMMAP,
+            info->kivmentries_sz, info->kivmentries,
+            sizeof(struct target_kinfo_vmentry));
+
+    /* NT_PROCSTAT_GROUPS */
+    info->groups = alloc_groups(&info->groups_sz);
+    if (info->groups == NULL) {
+        err = -TARGET_ENOMEM;
+        goto edone;
+    }
+    err = fill_groups(info->groups, &info->groups_sz);
+    if (err != 0) {
+        goto edone;
+    }
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_PROCSTAT_GROUPS,
+            info->groups_sz, info->groups,
+            sizeof(uint32_t));
+
+    /* NT_PROCSTAT_RLIMIT */
+    info->rlimits = g_malloc0(RLIM_NLIMITS * sizeof(struct rlimit));
+    if (info->rlimits == NULL) {
+        return -TARGET_ENOMEM;
+    }
+    err = fill_rlimits(info->rlimits);
+    if (err != 0) {
+        goto edone;
+    }
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_PROCSTAT_RLIMIT,
+            sizeof(struct rlimit) * RLIM_NLIMITS, info->rlimits,
+            sizeof(struct rlimit) * RLIM_NLIMITS);
+
+    /* NT_PROCSTAT_OSREL */
+    err = fill_osreldate(&info->osreldate);
+    if (err != 0) {
+        goto edone;
+    }
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_PROCSTAT_OSREL,
+            sizeof(info->osreldate), &info->osreldate,
+            sizeof(int32_t));
+
+    /* NT_PROCSTAT_PSSTRINGS */
+    err = fill_psstrings(&info->psstrings);
+    if (err != 0) {
+        goto edone;
+    }
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_PROCSTAT_PSSTRINGS,
+            sizeof(info->psstrings), &info->psstrings,
+            sizeof(abi_ulong));
+
+    /* NT_PROCSTAT_AUXV */
+    info->auxv = g_malloc0(target_auxents_sz);
+    if (info->auxv == NULL) {
+        err = -TARGET_ENOMEM;
+        goto edone;
+    }
+    err = fill_auxv(info->auxv, &info->auxv_sz);
+    if (err != 0) {
+        goto edone;
+    }
+    fill_note(&info->notes[numnotes++], "FreeBSD", TARGET_NT_PROCSTAT_AUXV,
+            info->auxv_sz, info->auxv,
+            sizeof(struct target_elf_auxinfo));
+
+    assert(numnotes <= MAXNUMNOTES);
+    info->numnote = numnotes;
+    info->notes_size = 0;
+    for (i = 0; i < numnotes; i++) {
+        info->notes_size += note_size(&info->notes[i]);
+    }
+
+    /* read and fill status of all threads */
+    cpu_list_lock();
+    CPU_FOREACH(cpu) {
+        if (cpu == thread_cpu) {
+            continue;
+        }
+        err = fill_thread_info(info, signr, (CPUArchState *)cpu->env_ptr);
+        if (err != 0) {
+            cpu_list_unlock();
+            goto edone;
+        }
+    }
+    cpu_list_unlock();
+
+    return 0;
+
+edone:
+    free_note_info(info);
+    return err;
+}
+
+static int elf_core_dump(int signr, CPUArchState *env)
+{
+    int fd = -1;
+    int segs = 0;
+    off_t offset = 0, data_offset = 0;
+    CPUState *cpu = env_cpu((CPUArchState *)env);
+    TaskState *ts = (TaskState *)cpu->opaque;
+    struct vm_area_struct *vma = NULL;
+    struct mm_struct *mm = NULL;
+    struct rlimit dumpsize;
+    struct elfhdr elf;
+    struct elf_phdr phdr;
+    struct elf_note_info info;
+    char corefile[PATH_MAX];
+
+    init_note_info(&info);
+
+    errno = 0;
+    getrlimit(RLIMIT_CORE, &dumpsize);
+    if (dumpsize.rlim_cur == 0) {
+        return 0;
+    }
+
+    if (core_dump_filename(ts, corefile, sizeof(corefile)) < 0) {
+        return -(errno);
+    }
+
+    fd = open(corefile, O_WRONLY | O_CREAT,
+              S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+    if (fd < 0) {
+        return -(errno);
+    }
+
+    /*
+     * Walk through target process memory mappings and
+     * set up structure containing this information.  After
+     * this point vma_xxx functions can be used.
+     */
+    mm = vma_init();
+    if (mm == NULL) {
+        goto out;
+    }
+
+    walk_memory_regions(mm, vma_walker);
+    segs = vma_get_mapping_count(mm);
+
+    /*
+     * Construct the coredump ELF header.  Add another segment for
+     * notes.
+     *
+     * See kern/imgact_elf.c  __elfN(corehdr)().
+     */
+    fill_elf_header(&elf, segs + 1, ELF_MACHINE, ts->info->elf_flags);
+    if (dump_write(fd, &elf, sizeof(elf)) != 0) {
+        goto out;
+    }
+
+    /*
+     * Construct the note segment and write it out.
+     */
+    if (fill_note_info(&info, signr, env) < 0) {
+        goto out;
+    }
+
+    offset += sizeof(elf);                             /* elf header */
+    offset += (segs + 1) * sizeof(struct elf_phdr);    /* program headers */
+
+    /* Write out notes program header. */
+    fill_elf_note_phdr(&phdr, info.notes_size, offset);
+
+    offset += info.notes_size;
+    if (dump_write(fd, &phdr, sizeof(phdr)) != 0) {
+        goto out;
+    }
+
+    /*
+     * ELF specification wants data to start at page boundary so
+     * we align it here.
+     */
+    data_offset = offset = roundup(offset, ELF_EXEC_PAGESIZE);
+
+    /*
+     * Write program headers for memory regions mapped in the
+     * target process.
+     *
+     * See cb_put_phdr() in sys/kern/imgact_ef.c
+     */
+    for (vma = vma_first(mm); vma != NULL; vma = vma_next(vma)) {
+        (void) memset(&phdr, 0, sizeof(phdr));
+
+        phdr.p_type = PT_LOAD;
+        phdr.p_offset = offset;
+        phdr.p_vaddr = vma->vma_start;
+        phdr.p_paddr = 0;
+        phdr.p_filesz = vma_dump_size(vma); /* ??? */
+        offset += phdr.p_filesz;
+        phdr.p_memsz = vma->vma_end - vma->vma_start;
+        phdr.p_flags = vma->vma_flags & PROT_READ ? PF_R : 0;
+        if (vma->vma_flags & PROT_WRITE) {
+            phdr.p_flags |= PF_W;
+        }
+        if (vma->vma_flags & PROT_EXEC) {
+            phdr.p_flags |= PF_X;
+        }
+        phdr.p_align = ELF_EXEC_PAGESIZE;  /* or PAGE_SIZE? */
+
+        bswap_phdr(&phdr, 1);
+        dump_write(fd, &phdr, sizeof(phdr));
+    }
+
+    /*
+     * Next write notes just after program headers.
+     */
+    if (write_note_info(&info, fd) < 0) {
+        goto out;
+    }
+
+    /*
+     * Align data to page boundary.
+     */
+    if (lseek(fd, data_offset, SEEK_SET) != data_offset) {
+        goto out;
+    }
+
+    /*
+     * Finally, dump the process memory into the corefile as well.
+     */
+    for (vma = vma_first(mm); vma != NULL; vma = vma_next(vma)) {
+        abi_ulong addr;
+        abi_ulong end;
+
+        end = vma->vma_start + vma_dump_size(vma);
+
+        for (addr = vma->vma_start; addr < end;
+                addr += TARGET_PAGE_SIZE) {
+            char page[TARGET_PAGE_SIZE];
+            int error;
+
+            /*
+             * Read in page from target process memory and
+             * write it to coredump file.
+             */
+            error = copy_from_user(page, addr, sizeof(page));
+            if (error != 0) {
+                (void) fprintf(stderr, "unable to dump " TARGET_ABI_FMT_lx "\n",
+                        addr);
+                errno = -error;
+                goto out;
+            }
+            if (dump_write(fd, page, TARGET_PAGE_SIZE) < 0) {
+                goto out;
+            }
+        }
+    }
+    errno = 0;
+
+out:
+    if (mm != NULL) {
+        vma_delete(mm);
+    }
+
+    (void)close(fd);
+
+    if (errno != 0) {
+        return -errno;
+    }
+    return 0;
+}
+
+#endif /* USE_ELF_CORE_DUMP */