diff options
Diffstat (limited to 'usr.bin/gcore/elfcore.c')
-rw-r--r-- | usr.bin/gcore/elfcore.c | 878 |
1 files changed, 878 insertions, 0 deletions
diff --git a/usr.bin/gcore/elfcore.c b/usr.bin/gcore/elfcore.c new file mode 100644 index 000000000000..2bffd409bb11 --- /dev/null +++ b/usr.bin/gcore/elfcore.c @@ -0,0 +1,878 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2017 Dell EMC + * Copyright (c) 2007 Sandvine Incorporated + * Copyright (c) 1998 John D. Polstra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#include <sys/endian.h> +#include <sys/param.h> +#include <sys/procfs.h> +#include <sys/ptrace.h> +#include <sys/queue.h> +#include <sys/linker_set.h> +#include <sys/sbuf.h> +#include <sys/sysctl.h> +#include <sys/user.h> +#include <sys/wait.h> +#include <machine/elf.h> +#include <vm/vm_param.h> +#include <vm/vm.h> +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <libutil.h> + +#include "extern.h" + +/* + * Code for generating ELF core dumps. + */ + +struct map_entry { + struct map_entry *next; + vm_offset_t start; + vm_offset_t end; + vm_prot_t protection; +}; + +typedef void (*segment_callback)(struct map_entry *, void *); + +/* Closure for cb_put_phdr(). */ +struct phdr_closure { + Elf_Phdr *phdr; /* Program header to fill in */ + Elf_Off offset; /* Offset of segment in core file */ +}; + +/* Closure for cb_size_segment(). */ +struct sseg_closure { + int count; /* Count of writable segments. */ + size_t size; /* Total size of all writable segments. */ +}; + +#ifdef ELFCORE_COMPAT_32 +typedef struct prpsinfo32 elfcore_prpsinfo_t; +#else +typedef prpsinfo_t elfcore_prpsinfo_t; +#endif + +typedef void* (*notefunc_t)(void *, size_t *); + +static void cb_put_phdr(struct map_entry *, void *); +static void cb_size_segment(struct map_entry *, void *); +static void each_dumpable_segment(struct map_entry *, segment_callback, + void *closure); +static void elf_detach(void); /* atexit() handler. */ +static void *elf_note_prpsinfo(void *, size_t *); +#if defined(__i386__) || defined(__amd64__) +static void *elf_note_x86_xstate(void *, size_t *); +#endif +#if defined(__powerpc__) +static void *elf_note_powerpc_vmx(void *, size_t *); +static void *elf_note_powerpc_vsx(void *, size_t *); +#endif +static void *elf_note_procstat_auxv(void *, size_t *); +static void *elf_note_procstat_files(void *, size_t *); +static void *elf_note_procstat_groups(void *, size_t *); +static void *elf_note_procstat_osrel(void *, size_t *); +static void *elf_note_procstat_proc(void *, size_t *); +static void *elf_note_procstat_psstrings(void *, size_t *); +static void *elf_note_procstat_rlimit(void *, size_t *); +static void *elf_note_procstat_umask(void *, size_t *); +static void *elf_note_procstat_vmmap(void *, size_t *); +static void elf_puthdr(int, pid_t, struct map_entry *, void *, size_t, size_t, + size_t, int); +static void elf_putnote(int, notefunc_t, void *, struct sbuf *); +static void elf_putnotes(pid_t, struct sbuf *, size_t *); +static void elf_putregnote(int, lwpid_t, struct sbuf *); +static void freemap(struct map_entry *); +static struct map_entry *readmap(pid_t); +static void *procstat_sysctl(void *, int, size_t, size_t *sizep); + +static pid_t g_pid; /* Pid being dumped, global for elf_detach */ +static int g_status; /* proc status after ptrace attach */ + +static int +elf_ident(int efd, pid_t pid __unused, char *binfile __unused) +{ + Elf_Ehdr hdr; + int cnt; + uint16_t machine; + + cnt = read(efd, &hdr, sizeof(hdr)); + if (cnt != sizeof(hdr)) + return (0); + if (!IS_ELF(hdr)) + return (0); + switch (hdr.e_ident[EI_DATA]) { + case ELFDATA2LSB: + machine = le16toh(hdr.e_machine); + break; + case ELFDATA2MSB: + machine = be16toh(hdr.e_machine); + break; + default: + return (0); + } + if (!ELF_MACHINE_OK(machine)) + return (0); + + /* Looks good. */ + return (1); +} + +static void +elf_detach(void) +{ + int sig; + + if (g_pid != 0) { + /* + * Forward any pending signals. SIGSTOP is generated by ptrace + * itself, so ignore it. + */ + sig = WIFSTOPPED(g_status) ? WSTOPSIG(g_status) : 0; + if (sig == SIGSTOP) + sig = 0; + ptrace(PT_DETACH, g_pid, (caddr_t)1, sig); + } +} + +/* + * Write an ELF coredump for the given pid to the given fd. + */ +static void +elf_coredump(int efd, int fd, pid_t pid) +{ + struct map_entry *map; + struct sseg_closure seginfo; + struct sbuf *sb; + void *hdr; + size_t hdrsize, notesz, segoff; + ssize_t n, old_len; + Elf_Phdr *php; + int i; + + /* Attach to process to dump. */ + g_pid = pid; + if (atexit(elf_detach) != 0) + err(1, "atexit"); + errno = 0; + ptrace(PT_ATTACH, pid, NULL, 0); + if (errno) + err(1, "PT_ATTACH"); + if (waitpid(pid, &g_status, 0) == -1) + err(1, "waitpid"); + + /* Get the program's memory map. */ + map = readmap(pid); + + /* Size the program segments. */ + seginfo.count = 0; + seginfo.size = 0; + each_dumpable_segment(map, cb_size_segment, &seginfo); + + /* + * Build the header and the notes using sbuf and write to the file. + */ + sb = sbuf_new_auto(); + hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); + if (seginfo.count + 1 >= PN_XNUM) + hdrsize += sizeof(Elf_Shdr); + /* Start header + notes section. */ + sbuf_start_section(sb, NULL); + /* Make empty header subsection. */ + sbuf_start_section(sb, &old_len); + sbuf_putc(sb, 0); + sbuf_end_section(sb, old_len, hdrsize, 0); + /* Put notes. */ + elf_putnotes(pid, sb, ¬esz); + /* Align up to a page boundary for the program segments. */ + sbuf_end_section(sb, -1, getpagesize(), 0); + if (sbuf_finish(sb) != 0) + err(1, "sbuf_finish"); + hdr = sbuf_data(sb); + segoff = sbuf_len(sb); + /* Fill in the header. */ + elf_puthdr(efd, pid, map, hdr, hdrsize, notesz, segoff, seginfo.count); + + n = write(fd, hdr, segoff); + if (n == -1) + err(1, "write"); + if (n < segoff) + errx(1, "short write"); + + /* Write the contents of all of the writable segments. */ + php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; + for (i = 0; i < seginfo.count; i++) { + struct ptrace_io_desc iorequest; + uintmax_t nleft = php->p_filesz; + + iorequest.piod_op = PIOD_READ_D; + iorequest.piod_offs = (caddr_t)(uintptr_t)php->p_vaddr; + while (nleft > 0) { + char buf[8*1024]; + size_t nwant; + ssize_t ngot; + + if (nleft > sizeof(buf)) + nwant = sizeof buf; + else + nwant = nleft; + iorequest.piod_addr = buf; + iorequest.piod_len = nwant; + ptrace(PT_IO, pid, (caddr_t)&iorequest, 0); + ngot = iorequest.piod_len; + if ((size_t)ngot < nwant) + errx(1, "short read wanted %zu, got %zd", + nwant, ngot); + ngot = write(fd, buf, nwant); + if (ngot == -1) + err(1, "write of segment %d failed", i); + if ((size_t)ngot != nwant) + errx(1, "short write"); + nleft -= nwant; + iorequest.piod_offs += ngot; + } + php++; + } + sbuf_delete(sb); + freemap(map); +} + +/* + * A callback for each_dumpable_segment() to write out the segment's + * program header entry. + */ +static void +cb_put_phdr(struct map_entry *entry, void *closure) +{ + struct phdr_closure *phc = (struct phdr_closure *)closure; + Elf_Phdr *phdr = phc->phdr; + size_t page_size; + + page_size = getpagesize(); + phc->offset = roundup2(phc->offset, page_size); + + phdr->p_type = PT_LOAD; + phdr->p_offset = phc->offset; + phdr->p_vaddr = entry->start; + phdr->p_paddr = 0; + phdr->p_filesz = phdr->p_memsz = entry->end - entry->start; + phdr->p_align = page_size; + phdr->p_flags = 0; + if (entry->protection & VM_PROT_READ) + phdr->p_flags |= PF_R; + if (entry->protection & VM_PROT_WRITE) + phdr->p_flags |= PF_W; + if (entry->protection & VM_PROT_EXECUTE) + phdr->p_flags |= PF_X; + + phc->offset += phdr->p_filesz; + phc->phdr++; +} + +/* + * A callback for each_dumpable_segment() to gather information about + * the number of segments and their total size. + */ +static void +cb_size_segment(struct map_entry *entry, void *closure) +{ + struct sseg_closure *ssc = (struct sseg_closure *)closure; + + ssc->count++; + ssc->size += entry->end - entry->start; +} + +/* + * For each segment in the given memory map, call the given function + * with a pointer to the map entry and some arbitrary caller-supplied + * data. + */ +static void +each_dumpable_segment(struct map_entry *map, segment_callback func, + void *closure) +{ + struct map_entry *entry; + + for (entry = map; entry != NULL; entry = entry->next) + (*func)(entry, closure); +} + +static void +elf_putnotes(pid_t pid, struct sbuf *sb, size_t *sizep) +{ + lwpid_t *tids; + size_t threads, old_len; + ssize_t size; + int i; + + errno = 0; + threads = ptrace(PT_GETNUMLWPS, pid, NULL, 0); + if (errno) + err(1, "PT_GETNUMLWPS"); + tids = malloc(threads * sizeof(*tids)); + if (tids == NULL) + errx(1, "out of memory"); + errno = 0; + ptrace(PT_GETLWPLIST, pid, (void *)tids, threads); + if (errno) + err(1, "PT_GETLWPLIST"); + + sbuf_start_section(sb, &old_len); + elf_putnote(NT_PRPSINFO, elf_note_prpsinfo, &pid, sb); + + for (i = 0; i < threads; ++i) { + elf_putregnote(NT_PRSTATUS, tids[i], sb); + elf_putregnote(NT_FPREGSET, tids[i], sb); + elf_putregnote(NT_THRMISC, tids[i], sb); + elf_putregnote(NT_PTLWPINFO, tids[i], sb); +#if defined(__aarch64__) || defined(__arm__) + elf_putregnote(NT_ARM_TLS, tids[i], sb); +#endif +#if (defined(ELFCORE_COMPAT_32) && defined(__aarch64__)) || defined(__arm__) + elf_putregnote(NT_ARM_VFP, tids[i], sb); +#endif +#if defined(__i386__) || defined(__amd64__) + elf_putregnote(NT_X86_SEGBASES, tids[i], sb); + elf_putnote(NT_X86_XSTATE, elf_note_x86_xstate, tids + i, sb); +#endif +#if defined(__powerpc__) + elf_putnote(NT_PPC_VMX, elf_note_powerpc_vmx, tids + i, sb); +#ifndef __SPE__ + elf_putnote(NT_PPC_VSX, elf_note_powerpc_vsx, tids + i, sb); +#endif +#endif + } + +#ifndef ELFCORE_COMPAT_32 + elf_putnote(NT_PROCSTAT_PROC, elf_note_procstat_proc, &pid, sb); + elf_putnote(NT_PROCSTAT_FILES, elf_note_procstat_files, &pid, sb); + elf_putnote(NT_PROCSTAT_VMMAP, elf_note_procstat_vmmap, &pid, sb); + elf_putnote(NT_PROCSTAT_GROUPS, elf_note_procstat_groups, &pid, sb); + elf_putnote(NT_PROCSTAT_UMASK, elf_note_procstat_umask, &pid, sb); + elf_putnote(NT_PROCSTAT_RLIMIT, elf_note_procstat_rlimit, &pid, sb); + elf_putnote(NT_PROCSTAT_OSREL, elf_note_procstat_osrel, &pid, sb); + elf_putnote(NT_PROCSTAT_PSSTRINGS, elf_note_procstat_psstrings, &pid, + sb); + elf_putnote(NT_PROCSTAT_AUXV, elf_note_procstat_auxv, &pid, sb); +#endif + + size = sbuf_end_section(sb, old_len, 1, 0); + if (size == -1) + err(1, "sbuf_end_section"); + free(tids); + *sizep = size; +} + +/* + * Emit one register set note section to sbuf. + */ +static void +elf_putregnote(int type, lwpid_t tid, struct sbuf *sb) +{ + Elf_Note note; + struct iovec iov; + ssize_t old_len; + + iov.iov_base = NULL; + iov.iov_len = 0; + if (ptrace(PT_GETREGSET, tid, (void *)&iov, type) != 0) + return; + iov.iov_base = calloc(1, iov.iov_len); + if (iov.iov_base == NULL) + errx(1, "out of memory"); + if (ptrace(PT_GETREGSET, tid, (void *)&iov, type) != 0) + errx(1, "failed to fetch register set %d", type); + + note.n_namesz = 8; /* strlen("FreeBSD") + 1 */ + note.n_descsz = iov.iov_len; + note.n_type = type; + + sbuf_bcat(sb, ¬e, sizeof(note)); + sbuf_start_section(sb, &old_len); + sbuf_bcat(sb, "FreeBSD", note.n_namesz); + sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); + sbuf_start_section(sb, &old_len); + sbuf_bcat(sb, iov.iov_base, iov.iov_len); + sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); + free(iov.iov_base); +} + +/* + * Emit one note section to sbuf. + */ +static void +elf_putnote(int type, notefunc_t notefunc, void *arg, struct sbuf *sb) +{ + Elf_Note note; + size_t descsz; + ssize_t old_len; + void *desc; + + desc = notefunc(arg, &descsz); + note.n_namesz = 8; /* strlen("FreeBSD") + 1 */ + note.n_descsz = descsz; + note.n_type = type; + + sbuf_bcat(sb, ¬e, sizeof(note)); + sbuf_start_section(sb, &old_len); + sbuf_bcat(sb, "FreeBSD", note.n_namesz); + sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); + if (descsz == 0) + return; + sbuf_start_section(sb, &old_len); + sbuf_bcat(sb, desc, descsz); + sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); + free(desc); +} + +/* + * Generate the ELF coredump header. + */ +static void +elf_puthdr(int efd, pid_t pid, struct map_entry *map, void *hdr, size_t hdrsize, + size_t notesz, size_t segoff, int numsegs) +{ + Elf_Ehdr *ehdr, binhdr; + Elf_Phdr *phdr; + Elf_Shdr *shdr; + struct phdr_closure phc; + ssize_t cnt; + + cnt = read(efd, &binhdr, sizeof(binhdr)); + if (cnt < 0) + err(1, "Failed to re-read ELF header"); + else if (cnt != sizeof(binhdr)) + errx(1, "Failed to re-read ELF header"); + + ehdr = (Elf_Ehdr *)hdr; + + 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_ident[EI_CLASS] = ELF_CLASS; + ehdr->e_ident[EI_DATA] = ELF_DATA; + ehdr->e_ident[EI_VERSION] = EV_CURRENT; + ehdr->e_ident[EI_OSABI] = ELFOSABI_FREEBSD; + ehdr->e_ident[EI_ABIVERSION] = 0; + ehdr->e_ident[EI_PAD] = 0; + ehdr->e_type = ET_CORE; + ehdr->e_machine = binhdr.e_machine; + ehdr->e_version = EV_CURRENT; + ehdr->e_entry = 0; + ehdr->e_phoff = sizeof(Elf_Ehdr); + ehdr->e_flags = binhdr.e_flags; + ehdr->e_ehsize = sizeof(Elf_Ehdr); + ehdr->e_phentsize = sizeof(Elf_Phdr); + ehdr->e_shentsize = sizeof(Elf_Shdr); + ehdr->e_shstrndx = SHN_UNDEF; + if (numsegs + 1 < PN_XNUM) { + ehdr->e_phnum = numsegs + 1; + ehdr->e_shnum = 0; + } else { + ehdr->e_phnum = PN_XNUM; + ehdr->e_shnum = 1; + + ehdr->e_shoff = ehdr->e_phoff + + (numsegs + 1) * ehdr->e_phentsize; + + shdr = (Elf_Shdr *)((char *)hdr + ehdr->e_shoff); + memset(shdr, 0, sizeof(*shdr)); + /* + * A special first section is used to hold large segment and + * section counts. This was proposed by Sun Microsystems in + * Solaris and has been adopted by Linux; the standard ELF + * tools are already familiar with the technique. + * + * See table 7-7 of the Solaris "Linker and Libraries Guide" + * (or 12-7 depending on the version of the document) for more + * details. + */ + shdr->sh_type = SHT_NULL; + shdr->sh_size = ehdr->e_shnum; + shdr->sh_link = ehdr->e_shstrndx; + shdr->sh_info = numsegs + 1; + } + + /* + * Fill in the program header entries. + */ + phdr = (Elf_Phdr *)((char *)hdr + ehdr->e_phoff); + + /* The note segment. */ + phdr->p_type = PT_NOTE; + phdr->p_offset = hdrsize; + phdr->p_vaddr = 0; + phdr->p_paddr = 0; + phdr->p_filesz = notesz; + phdr->p_memsz = 0; + phdr->p_flags = PF_R; + phdr->p_align = sizeof(Elf32_Size); + phdr++; + + /* All the writable segments from the program. */ + phc.phdr = phdr; + phc.offset = segoff; + each_dumpable_segment(map, cb_put_phdr, &phc); +} + +/* + * Free the memory map. + */ +static void +freemap(struct map_entry *map) +{ + struct map_entry *next; + + while (map != NULL) { + next = map->next; + free(map); + map = next; + } +} + +/* + * Read the process's memory map using kinfo_getvmmap(), and return a list of + * VM map entries. Only the non-device read/writable segments are + * returned. The map entries in the list aren't fully filled in; only + * the items we need are present. + */ +static struct map_entry * +readmap(pid_t pid) +{ + struct map_entry *ent, **linkp, *map; + struct kinfo_vmentry *vmentl, *kve; + int i, nitems; + + vmentl = kinfo_getvmmap(pid, &nitems); + if (vmentl == NULL) + err(1, "cannot retrieve mappings for %u process", pid); + + map = NULL; + linkp = ↦ + for (i = 0; i < nitems; i++) { + kve = &vmentl[i]; + + /* + * Ignore 'malformed' segments or ones representing memory + * mapping with MAP_NOCORE on. + * If the 'full' support is disabled, just dump the most + * meaningful data segments. + */ + if ((kve->kve_protection & KVME_PROT_READ) == 0 || + (kve->kve_flags & KVME_FLAG_NOCOREDUMP) != 0 || + kve->kve_type == KVME_TYPE_DEAD || + kve->kve_type == KVME_TYPE_UNKNOWN || + ((pflags & PFLAGS_FULL) == 0 && + kve->kve_type != KVME_TYPE_DEFAULT && + kve->kve_type != KVME_TYPE_VNODE && + kve->kve_type != KVME_TYPE_SWAP && + kve->kve_type != KVME_TYPE_PHYS)) + continue; + + ent = calloc(1, sizeof(*ent)); + if (ent == NULL) + errx(1, "out of memory"); + ent->start = (vm_offset_t)kve->kve_start; + ent->end = (vm_offset_t)kve->kve_end; + ent->protection = VM_PROT_READ; + if ((kve->kve_protection & KVME_PROT_WRITE) != 0) + ent->protection |= VM_PROT_WRITE; + if ((kve->kve_protection & KVME_PROT_EXEC) != 0) + ent->protection |= VM_PROT_EXECUTE; + + *linkp = ent; + linkp = &ent->next; + } + free(vmentl); + return (map); +} + +/* + * Miscellaneous note out functions. + */ + +static void * +elf_note_prpsinfo(void *arg, size_t *sizep) +{ + char *cp, *end; + pid_t pid; + elfcore_prpsinfo_t *psinfo; + struct kinfo_proc kip; + size_t len; + int name[4]; + + pid = *(pid_t *)arg; + psinfo = calloc(1, sizeof(*psinfo)); + if (psinfo == NULL) + errx(1, "out of memory"); + psinfo->pr_version = PRPSINFO_VERSION; + psinfo->pr_psinfosz = sizeof(*psinfo); + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_PID; + name[3] = pid; + len = sizeof(kip); + if (sysctl(name, 4, &kip, &len, NULL, 0) == -1) + err(1, "kern.proc.pid.%u", pid); + if (kip.ki_pid != pid) + err(1, "kern.proc.pid.%u", pid); + strlcpy(psinfo->pr_fname, kip.ki_comm, sizeof(psinfo->pr_fname)); + name[2] = KERN_PROC_ARGS; + len = sizeof(psinfo->pr_psargs) - 1; + if (sysctl(name, 4, psinfo->pr_psargs, &len, NULL, 0) == 0 && len > 0) { + cp = psinfo->pr_psargs; + end = cp + len - 1; + for (;;) { + cp = memchr(cp, '\0', end - cp); + if (cp == NULL) + break; + *cp = ' '; + } + } else + strlcpy(psinfo->pr_psargs, kip.ki_comm, + sizeof(psinfo->pr_psargs)); + psinfo->pr_pid = pid; + + *sizep = sizeof(*psinfo); + return (psinfo); +} + +#if defined(__i386__) || defined(__amd64__) +static void * +elf_note_x86_xstate(void *arg, size_t *sizep) +{ + lwpid_t tid; + char *xstate; + static bool xsave_checked = false; + static struct ptrace_xstate_info info; + + tid = *(lwpid_t *)arg; + if (!xsave_checked) { + if (ptrace(PT_GETXSTATE_INFO, tid, (void *)&info, + sizeof(info)) != 0) + info.xsave_len = 0; + xsave_checked = true; + } + if (info.xsave_len == 0) { + *sizep = 0; + return (NULL); + } + xstate = calloc(1, info.xsave_len); + ptrace(PT_GETXSTATE, tid, xstate, 0); + *(uint64_t *)(xstate + X86_XSTATE_XCR0_OFFSET) = info.xsave_mask; + *sizep = info.xsave_len; + return (xstate); +} +#endif + +#if defined(__powerpc__) +static void * +elf_note_powerpc_vmx(void *arg, size_t *sizep) +{ + lwpid_t tid; + struct vmxreg *vmx; + static bool has_vmx = true; + struct vmxreg info; + + tid = *(lwpid_t *)arg; + if (has_vmx) { + if (ptrace(PT_GETVRREGS, tid, (void *)&info, + sizeof(info)) != 0) + has_vmx = false; + } + if (!has_vmx) { + *sizep = 0; + return (NULL); + } + vmx = calloc(1, sizeof(*vmx)); + memcpy(vmx, &info, sizeof(*vmx)); + *sizep = sizeof(*vmx); + return (vmx); +} + +static void * +elf_note_powerpc_vsx(void *arg, size_t *sizep) +{ + lwpid_t tid; + char *vshr_data; + static bool has_vsx = true; + uint64_t vshr[32]; + + tid = *(lwpid_t *)arg; + if (has_vsx) { + if (ptrace(PT_GETVSRREGS, tid, (void *)vshr, + sizeof(vshr)) != 0) + has_vsx = false; + } + if (!has_vsx) { + *sizep = 0; + return (NULL); + } + vshr_data = calloc(1, sizeof(vshr)); + memcpy(vshr_data, vshr, sizeof(vshr)); + *sizep = sizeof(vshr); + return (vshr_data); +} +#endif + +static void * +procstat_sysctl(void *arg, int what, size_t structsz, size_t *sizep) +{ + size_t len; + pid_t pid; + int name[4], structsize; + void *buf, *p; + + pid = *(pid_t *)arg; + structsize = structsz; + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = what; + name[3] = pid; + len = 0; + if (sysctl(name, 4, NULL, &len, NULL, 0) == -1) + err(1, "kern.proc.%d.%u", what, pid); + buf = calloc(1, sizeof(structsize) + len * 4 / 3); + if (buf == NULL) + errx(1, "out of memory"); + bcopy(&structsize, buf, sizeof(structsize)); + p = (char *)buf + sizeof(structsize); + if (sysctl(name, 4, p, &len, NULL, 0) == -1) + err(1, "kern.proc.%d.%u", what, pid); + + *sizep = sizeof(structsize) + len; + return (buf); +} + +static void * +elf_note_procstat_proc(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_PID | KERN_PROC_INC_THREAD, + sizeof(struct kinfo_proc), sizep)); +} + +static void * +elf_note_procstat_files(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_FILEDESC, + sizeof(struct kinfo_file), sizep)); +} + +static void * +elf_note_procstat_vmmap(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_VMMAP, + sizeof(struct kinfo_vmentry), sizep)); +} + +static void * +elf_note_procstat_groups(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_GROUPS, sizeof(gid_t), sizep)); +} + +static void * +elf_note_procstat_umask(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_UMASK, sizeof(u_short), sizep)); +} + +static void * +elf_note_procstat_osrel(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_OSREL, sizeof(int), sizep)); +} + +static void * +elf_note_procstat_psstrings(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_PS_STRINGS, + sizeof(vm_offset_t), sizep)); +} + +static void * +elf_note_procstat_auxv(void *arg, size_t *sizep) +{ + + return (procstat_sysctl(arg, KERN_PROC_AUXV, + sizeof(Elf_Auxinfo), sizep)); +} + +static void * +elf_note_procstat_rlimit(void *arg, size_t *sizep) +{ + pid_t pid; + size_t len; + int i, name[5], structsize; + void *buf, *p; + + pid = *(pid_t *)arg; + structsize = sizeof(struct rlimit) * RLIM_NLIMITS; + buf = calloc(1, sizeof(structsize) + structsize); + if (buf == NULL) + errx(1, "out of memory"); + bcopy(&structsize, buf, sizeof(structsize)); + p = (char *)buf + sizeof(structsize); + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_RLIMIT; + name[3] = pid; + len = sizeof(struct rlimit); + for (i = 0; i < RLIM_NLIMITS; i++) { + name[4] = i; + if (sysctl(name, 5, p, &len, NULL, 0) == -1) + err(1, "kern.proc.rlimit.%u", pid); + if (len != sizeof(struct rlimit)) + errx(1, "kern.proc.rlimit.%u: short read", pid); + p += len; + } + + *sizep = sizeof(structsize) + structsize; + return (buf); +} + +struct dumpers __elfN(dump) = { elf_ident, elf_coredump }; +TEXT_SET(dumpset, __elfN(dump)); |