diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c index 813e37a559e8..f4659c39abe3 100644 --- a/sys/kern/imgact_elf.c +++ b/sys/kern/imgact_elf.c @@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -104,8 +105,8 @@ SYSCTL_NODE(_kern, OID_AUTO, __CONCAT(elf, __ELF_WORD_SIZE), CTLFLAG_RW, 0, #ifdef COMPRESS_USER_CORES static int compress_core(gzFile, char *, char *, unsigned int, struct thread * td); -#define CORE_BUF_SIZE (16 * 1024) #endif +#define CORE_BUF_SIZE (16 * 1024) int __elfN(fallback_brand) = -1; SYSCTL_INT(__CONCAT(_kern_elf, __ELF_WORD_SIZE), OID_AUTO, @@ -1038,14 +1039,35 @@ struct sseg_closure { size_t size; /* Total size of all writable segments. */ }; +typedef void (*outfunc_t)(void *, struct sbuf *, size_t *); + +struct note_info { + int type; /* Note type. */ + outfunc_t outfunc; /* Output function. */ + void *outarg; /* Argument for the output function. */ + size_t outsize; /* Output size. */ + TAILQ_ENTRY(note_info) link; /* Link to the next note info. */ +}; + +TAILQ_HEAD(note_info_list, note_info); + static void cb_put_phdr(vm_map_entry_t, void *); static void cb_size_segment(vm_map_entry_t, void *); static void each_writable_segment(struct thread *, segment_callback, void *); static int __elfN(corehdr)(struct thread *, struct vnode *, struct ucred *, - int, void *, size_t, gzFile); -static void __elfN(puthdr)(struct thread *, void *, size_t *, int); -static void __elfN(putnote)(void *, size_t *, const char *, int, - const void *, size_t); + int, void *, size_t, struct note_info_list *, size_t, gzFile); +static void __elfN(prepare_notes)(struct thread *, struct note_info_list *, + size_t *); +static void __elfN(puthdr)(struct thread *, void *, size_t, int, size_t); +static void __elfN(putnote)(struct note_info *, struct sbuf *); +static size_t register_note(struct note_info_list *, int, outfunc_t, void *); +static int sbuf_drain_core_output(void *, const char *, int); + +static void __elfN(note_fpregset)(void *, struct sbuf *, size_t *); +static void __elfN(note_prpsinfo)(void *, struct sbuf *, size_t *); +static void __elfN(note_prstatus)(void *, struct sbuf *, size_t *); +static void __elfN(note_threadmd)(void *, struct sbuf *, size_t *); +static void __elfN(note_thrmisc)(void *, struct sbuf *, size_t *); #ifdef COMPRESS_USER_CORES extern int compress_user_cores; @@ -1072,14 +1094,54 @@ core_output(struct vnode *vp, void *base, size_t len, off_t offset, return (error); } +/* Coredump output parameters for sbuf drain routine. */ +struct sbuf_drain_core_params { + off_t offset; + struct ucred *active_cred; + struct ucred *file_cred; + struct thread *td; + struct vnode *vp; +#ifdef COMPRESS_USER_CORES + gzFile gzfile; +#endif +}; + +/* + * Drain into a core file. + */ +static int +sbuf_drain_core_output(void *arg, const char *data, int len) +{ + struct sbuf_drain_core_params *p; + int error; + + p = (struct sbuf_drain_core_params *)arg; +#ifdef COMPRESS_USER_CORES + if (p->gzfile != Z_NULL) + error = compress_core(p->gzfile, NULL, __DECONST(char *, data), + len, p->td); + else +#endif + error = vn_rdwr_inchunks(UIO_WRITE, p->vp, + __DECONST(void *, data), len, p->offset, UIO_SYSSPACE, + IO_UNIT | IO_DIRECT, p->active_cred, p->file_cred, NULL, + p->td); + if (error != 0) + return (-error); + p->offset += len; + return (len); +} + int __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) { struct ucred *cred = td->td_ucred; int error = 0; struct sseg_closure seginfo; + struct note_info_list notelst; + struct note_info *ninfo; void *hdr; - size_t hdrsize; + size_t hdrsize, notesz, coresize; gzFile gzfile = Z_NULL; char *core_buf = NULL; @@ -1090,6 +1152,7 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) #endif hdr = NULL; + TAILQ_INIT(¬elst); #ifdef COMPRESS_USER_CORES if (doing_compress) { @@ -1118,30 +1181,29 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) each_writable_segment(td, cb_size_segment, &seginfo); /* - * Calculate the size of the core file header area by making - * a dry run of generating it. Nothing is written, but the - * size is calculated. + * Collect info about the core file header area. */ - hdrsize = 0; - __elfN(puthdr)(td, (void *)NULL, &hdrsize, seginfo.count); + hdrsize = sizeof(Elf_Ehdr) + sizeof(Elf_Phdr) * (1 + seginfo.count); + __elfN(prepare_notes)(td, ¬elst, ¬esz); + coresize = round_page(hdrsize + notesz) + seginfo.size; #ifdef RACCT PROC_LOCK(td->td_proc); - error = racct_add(td->td_proc, RACCT_CORE, hdrsize + seginfo.size); + error = racct_add(td->td_proc, RACCT_CORE, coresize); PROC_UNLOCK(td->td_proc); if (error != 0) { error = EFAULT; goto done; } #endif - if (hdrsize + seginfo.size >= limit) { + if (coresize >= limit) { error = EFAULT; goto done; } /* * Allocate memory for building the header, fill it up, - * and write it out. + * and write it out following the notes. */ hdr = malloc(hdrsize, M_TEMP, M_WAITOK); if (hdr == NULL) { @@ -1149,7 +1211,7 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) goto done; } error = __elfN(corehdr)(td, vp, cred, seginfo.count, hdr, hdrsize, - gzfile); + ¬elst, notesz, gzfile); /* Write the contents of all of the writable segments. */ if (error == 0) { @@ -1158,7 +1220,7 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) int i; php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; - offset = hdrsize; + offset = round_page(hdrsize + notesz); for (i = 0; i < seginfo.count; i++) { error = core_output(vp, (caddr_t)(uintptr_t)php->p_vaddr, php->p_filesz, offset, cred, NOCRED, curthread, core_buf, gzfile); @@ -1181,8 +1243,12 @@ __elfN(coredump)(struct thread *td, struct vnode *vp, off_t limit, int flags) if (gzfile) gzclose(gzfile); #endif - - free(hdr, M_TEMP); + while ((ninfo = TAILQ_FIRST(¬elst)) != NULL) { + TAILQ_REMOVE(¬elst, ninfo, link); + free(ninfo, M_TEMP); + } + if (hdr != NULL) + free(hdr, M_TEMP); return (error); } @@ -1299,44 +1365,194 @@ each_writable_segment(td, func, closure) * the page boundary. */ static int -__elfN(corehdr)(td, vp, cred, numsegs, hdr, hdrsize, gzfile) - struct thread *td; - struct vnode *vp; - struct ucred *cred; - int numsegs; - size_t hdrsize; - void *hdr; - gzFile gzfile; +__elfN(corehdr)(struct thread *td, struct vnode *vp, struct ucred *cred, + int numsegs, void *hdr, size_t hdrsize, struct note_info_list *notelst, + size_t notesz, gzFile gzfile) { - size_t off; + struct sbuf_drain_core_params params; + struct note_info *ninfo; + struct sbuf *sb; + int error; /* Fill in the header. */ bzero(hdr, hdrsize); - off = 0; - __elfN(puthdr)(td, hdr, &off, numsegs); + __elfN(puthdr)(td, hdr, hdrsize, numsegs, notesz); - if (!gzfile) { - /* Write it to the core file. */ - return (vn_rdwr_inchunks(UIO_WRITE, vp, hdr, hdrsize, (off_t)0, - UIO_SYSSPACE, IO_UNIT | IO_DIRECT, cred, NOCRED, NULL, - td)); - } else { + params.offset = 0; + params.active_cred = cred; + params.file_cred = NOCRED; + params.td = td; + params.vp = vp; #ifdef COMPRESS_USER_CORES - if (gzwrite(gzfile, hdr, hdrsize) != hdrsize) { - log(LOG_WARNING, - "Failed to compress core file header for process" - " %s.\n", curproc->p_comm); - return (EFAULT); - } - else { - return (0); - } -#else - panic("shouldn't be here"); + params.gzfile = gzfile; #endif - } + sb = sbuf_new(NULL, NULL, CORE_BUF_SIZE, SBUF_FIXEDLEN); + sbuf_set_drain(sb, sbuf_drain_core_output, ¶ms); + sbuf_start_section(sb, NULL); + sbuf_bcat(sb, hdr, hdrsize); + TAILQ_FOREACH(ninfo, notelst, link) + __elfN(putnote)(ninfo, sb); + /* Align up to a page boundary for the program segments. */ + sbuf_end_section(sb, -1, PAGE_SIZE, 0); + error = sbuf_finish(sb); + sbuf_delete(sb); + + return (error); } +static void +__elfN(prepare_notes)(struct thread *td, struct note_info_list *list, + size_t *sizep) +{ + struct proc *p; + struct thread *thr; + size_t size; + + p = td->td_proc; + size = 0; + + size += register_note(list, NT_PRPSINFO, __elfN(note_prpsinfo), p); + + /* + * To have the debugger select the right thread (LWP) as the initial + * thread, we dump the state of the thread passed to us in td first. + * This is the thread that causes the core dump and thus likely to + * be the right thread one wants to have selected in the debugger. + */ + thr = td; + while (thr != NULL) { + size += register_note(list, NT_PRSTATUS, + __elfN(note_prstatus), thr); + size += register_note(list, NT_FPREGSET, + __elfN(note_fpregset), thr); + size += register_note(list, NT_THRMISC, + __elfN(note_thrmisc), thr); + size += register_note(list, -1, + __elfN(note_threadmd), thr); + + thr = (thr == td) ? TAILQ_FIRST(&p->p_threads) : + TAILQ_NEXT(thr, td_plist); + if (thr == td) + thr = TAILQ_NEXT(thr, td_plist); + } + + *sizep = size; +} + +static void +__elfN(puthdr)(struct thread *td, void *hdr, size_t hdrsize, int numsegs, + size_t notesz) +{ + Elf_Ehdr *ehdr; + Elf_Phdr *phdr; + struct phdr_closure phc; + + ehdr = (Elf_Ehdr *)hdr; + phdr = (Elf_Phdr *)((char *)hdr + sizeof(Elf_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_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; +#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 + ehdr->e_machine = ELF_ARCH32; +#else + ehdr->e_machine = ELF_ARCH; +#endif + ehdr->e_version = EV_CURRENT; + ehdr->e_entry = 0; + ehdr->e_phoff = sizeof(Elf_Ehdr); + ehdr->e_flags = 0; + ehdr->e_ehsize = sizeof(Elf_Ehdr); + ehdr->e_phentsize = sizeof(Elf_Phdr); + ehdr->e_phnum = numsegs + 1; + ehdr->e_shentsize = sizeof(Elf_Shdr); + ehdr->e_shnum = 0; + ehdr->e_shstrndx = SHN_UNDEF; + + /* + * Fill in the program header entries. + */ + + /* The note segement. */ + 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 = round_page(hdrsize + notesz); + each_writable_segment(td, cb_put_phdr, &phc); +} + +static size_t +register_note(struct note_info_list *list, int type, outfunc_t out, void *arg) +{ + struct note_info *ninfo; + size_t size, notesize; + + size = 0; + out(arg, NULL, &size); + ninfo = malloc(sizeof(*ninfo), M_TEMP, M_ZERO | M_WAITOK); + ninfo->type = type; + ninfo->outfunc = out; + ninfo->outarg = arg; + ninfo->outsize = size; + TAILQ_INSERT_TAIL(list, ninfo, link); + + if (type == -1) + return (size); + + notesize = sizeof(Elf_Note) + /* note header */ + roundup2(8, sizeof(Elf32_Size)) + /* note name ("FreeBSD") */ + roundup2(size, sizeof(Elf32_Size)); /* note description */ + + return (notesize); +} + +static void +__elfN(putnote)(struct note_info *ninfo, struct sbuf *sb) +{ + Elf_Note note; + ssize_t old_len; + + if (ninfo->type == -1) { + ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); + return; + } + + note.n_namesz = 8; /* strlen("FreeBSD") + 1 */ + note.n_descsz = ninfo->outsize; + note.n_type = ninfo->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 (note.n_descsz == 0) + return; + sbuf_start_section(sb, &old_len); + ninfo->outfunc(ninfo->outarg, sb, &ninfo->outsize); + sbuf_end_section(sb, old_len, sizeof(Elf32_Size), 0); +} + +/* + * Miscellaneous note out functions. + */ + #if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 #include @@ -1356,50 +1572,15 @@ typedef thrmisc_t elf_thrmisc_t; #endif static void -__elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs) +__elfN(note_prpsinfo)(void *arg, struct sbuf *sb, size_t *sizep) { - struct { - elf_prstatus_t status; - elf_prfpregset_t fpregset; - elf_prpsinfo_t psinfo; - elf_thrmisc_t thrmisc; - } *tempdata; - elf_prstatus_t *status; - elf_prfpregset_t *fpregset; - elf_prpsinfo_t *psinfo; - elf_thrmisc_t *thrmisc; struct proc *p; - struct thread *thr; - size_t ehoff, noteoff, notesz, phoff; + elf_prpsinfo_t *psinfo; - p = td->td_proc; - - ehoff = *off; - *off += sizeof(Elf_Ehdr); - - phoff = *off; - *off += (numsegs + 1) * sizeof(Elf_Phdr); - - noteoff = *off; - /* - * Don't allocate space for the notes if we're just calculating - * the size of the header. We also don't collect the data. - */ - if (dst != NULL) { - tempdata = malloc(sizeof(*tempdata), M_TEMP, M_ZERO|M_WAITOK); - status = &tempdata->status; - fpregset = &tempdata->fpregset; - psinfo = &tempdata->psinfo; - thrmisc = &tempdata->thrmisc; - } else { - tempdata = NULL; - status = NULL; - fpregset = NULL; - psinfo = NULL; - thrmisc = NULL; - } - - if (dst != NULL) { + p = (struct proc *)arg; + if (sb != NULL) { + KASSERT(*sizep == sizeof(*psinfo), ("invalid size")); + psinfo = malloc(sizeof(*psinfo), M_TEMP, M_ZERO | M_WAITOK); psinfo->pr_version = PRPSINFO_VERSION; psinfo->pr_psinfosz = sizeof(elf_prpsinfo_t); strlcpy(psinfo->pr_fname, p->p_comm, sizeof(psinfo->pr_fname)); @@ -1409,139 +1590,100 @@ __elfN(puthdr)(struct thread *td, void *dst, size_t *off, int numsegs) */ strlcpy(psinfo->pr_psargs, p->p_comm, sizeof(psinfo->pr_psargs)); + + sbuf_bcat(sb, psinfo, sizeof(*psinfo)); + free(psinfo, M_TEMP); } - __elfN(putnote)(dst, off, "FreeBSD", NT_PRPSINFO, psinfo, - sizeof *psinfo); - - /* - * To have the debugger select the right thread (LWP) as the initial - * thread, we dump the state of the thread passed to us in td first. - * This is the thread that causes the core dump and thus likely to - * be the right thread one wants to have selected in the debugger. - */ - thr = td; - while (thr != NULL) { - if (dst != NULL) { - status->pr_version = PRSTATUS_VERSION; - status->pr_statussz = sizeof(elf_prstatus_t); - status->pr_gregsetsz = sizeof(elf_gregset_t); - status->pr_fpregsetsz = sizeof(elf_fpregset_t); - status->pr_osreldate = osreldate; - status->pr_cursig = p->p_sig; - status->pr_pid = thr->td_tid; -#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 - fill_regs32(thr, &status->pr_reg); - fill_fpregs32(thr, fpregset); -#else - fill_regs(thr, &status->pr_reg); - fill_fpregs(thr, fpregset); -#endif - memset(&thrmisc->_pad, 0, sizeof (thrmisc->_pad)); - strcpy(thrmisc->pr_tname, thr->td_name); - } - __elfN(putnote)(dst, off, "FreeBSD", NT_PRSTATUS, status, - sizeof *status); - __elfN(putnote)(dst, off, "FreeBSD", NT_FPREGSET, fpregset, - sizeof *fpregset); - __elfN(putnote)(dst, off, "FreeBSD", NT_THRMISC, thrmisc, - sizeof *thrmisc); - /* - * Allow for MD specific notes, as well as any MD - * specific preparations for writing MI notes. - */ - __elfN(dump_thread)(thr, dst, off); - - thr = (thr == td) ? TAILQ_FIRST(&p->p_threads) : - TAILQ_NEXT(thr, td_plist); - if (thr == td) - thr = TAILQ_NEXT(thr, td_plist); - } - - notesz = *off - noteoff; - - if (dst != NULL) - free(tempdata, M_TEMP); - - /* Align up to a page boundary for the program segments. */ - *off = round_page(*off); - - if (dst != NULL) { - Elf_Ehdr *ehdr; - Elf_Phdr *phdr; - struct phdr_closure phc; - - /* - * Fill in the ELF header. - */ - ehdr = (Elf_Ehdr *)((char *)dst + ehoff); - 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; -#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 - ehdr->e_machine = ELF_ARCH32; -#else - ehdr->e_machine = ELF_ARCH; -#endif - ehdr->e_version = EV_CURRENT; - ehdr->e_entry = 0; - ehdr->e_phoff = phoff; - ehdr->e_flags = 0; - ehdr->e_ehsize = sizeof(Elf_Ehdr); - ehdr->e_phentsize = sizeof(Elf_Phdr); - ehdr->e_phnum = numsegs + 1; - ehdr->e_shentsize = sizeof(Elf_Shdr); - ehdr->e_shnum = 0; - ehdr->e_shstrndx = SHN_UNDEF; - - /* - * Fill in the program header entries. - */ - phdr = (Elf_Phdr *)((char *)dst + phoff); - - /* The note segement. */ - phdr->p_type = PT_NOTE; - phdr->p_offset = noteoff; - 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 = *off; - each_writable_segment(td, cb_put_phdr, &phc); - } + *sizep = sizeof(*psinfo); } static void -__elfN(putnote)(void *dst, size_t *off, const char *name, int type, - const void *desc, size_t descsz) +__elfN(note_prstatus)(void *arg, struct sbuf *sb, size_t *sizep) { - Elf_Note note; + struct thread *td; + elf_prstatus_t *status; - note.n_namesz = strlen(name) + 1; - note.n_descsz = descsz; - note.n_type = type; - if (dst != NULL) - bcopy(¬e, (char *)dst + *off, sizeof note); - *off += sizeof note; - if (dst != NULL) - bcopy(name, (char *)dst + *off, note.n_namesz); - *off += roundup2(note.n_namesz, sizeof(Elf32_Size)); - if (dst != NULL) - bcopy(desc, (char *)dst + *off, note.n_descsz); - *off += roundup2(note.n_descsz, sizeof(Elf32_Size)); + td = (struct thread *)arg; + if (sb != NULL) { + KASSERT(*sizep == sizeof(*status), ("invalid size")); + status = malloc(sizeof(*status), M_TEMP, M_ZERO | M_WAITOK); + status->pr_version = PRSTATUS_VERSION; + status->pr_statussz = sizeof(elf_prstatus_t); + status->pr_gregsetsz = sizeof(elf_gregset_t); + status->pr_fpregsetsz = sizeof(elf_fpregset_t); + status->pr_osreldate = osreldate; + status->pr_cursig = td->td_proc->p_sig; + status->pr_pid = td->td_tid; +#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 + fill_regs32(td, &status->pr_reg); +#else + fill_regs(td, &status->pr_reg); +#endif + sbuf_bcat(sb, status, sizeof(*status)); + free(status, M_TEMP); + } + *sizep = sizeof(*status); +} + +static void +__elfN(note_fpregset)(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct thread *td; + elf_prfpregset_t *fpregset; + + td = (struct thread *)arg; + if (sb != NULL) { + KASSERT(*sizep == sizeof(*fpregset), ("invalid size")); + fpregset = malloc(sizeof(*fpregset), M_TEMP, M_ZERO | M_WAITOK); +#if defined(COMPAT_FREEBSD32) && __ELF_WORD_SIZE == 32 + fill_fpregs32(td, fpregset); +#else + fill_fpregs(td, fpregset); +#endif + sbuf_bcat(sb, fpregset, sizeof(*fpregset)); + free(fpregset, M_TEMP); + } + *sizep = sizeof(*fpregset); +} + +static void +__elfN(note_thrmisc)(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct thread *td; + elf_thrmisc_t thrmisc; + + td = (struct thread *)arg; + if (sb != NULL) { + KASSERT(*sizep == sizeof(thrmisc), ("invalid size")); + bzero(&thrmisc._pad, sizeof(thrmisc._pad)); + strcpy(thrmisc.pr_tname, td->td_name); + sbuf_bcat(sb, &thrmisc, sizeof(thrmisc)); + } + *sizep = sizeof(thrmisc); +} + +/* + * Allow for MD specific notes, as well as any MD + * specific preparations for writing MI notes. + */ +static void +__elfN(note_threadmd)(void *arg, struct sbuf *sb, size_t *sizep) +{ + struct thread *td; + void *buf; + size_t size; + + td = (struct thread *)arg; + size = *sizep; + buf = NULL; + if (size != 0 && sb != NULL) + buf = malloc(size, M_TEMP, M_ZERO | M_WAITOK); + size = 0; + __elfN(dump_thread)(td, buf, &size); + KASSERT(*sizep == size, ("invalid size")); + if (size != 0 && sb != NULL) + sbuf_bcat(sb, buf, size); + *sizep = size; } static boolean_t @@ -1636,6 +1778,8 @@ EXEC_SET(__CONCAT(elf, __ELF_WORD_SIZE), __elfN(execsw)); * routine gzwrite(). This copying is necessary because the content of the VM * segment may change between the compression pass and the crc-computation pass * in gzwrite(). This is because realtime threads may preempt the UNIX kernel. + * + * If inbuf is NULL it is assumed that data is already copied to 'dest_buf'. */ static int compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len, @@ -1646,8 +1790,13 @@ compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len, unsigned int chunk_len; while (len) { - chunk_len = (len > CORE_BUF_SIZE) ? CORE_BUF_SIZE : len; - copyin(inbuf, dest_buf, chunk_len); + if (inbuf != NULL) { + chunk_len = (len > CORE_BUF_SIZE) ? CORE_BUF_SIZE : len; + copyin(inbuf, dest_buf, chunk_len); + inbuf += chunk_len; + } else { + chunk_len = len; + } len_compressed = gzwrite(file, dest_buf, chunk_len); EVENTHANDLER_INVOKE(app_coredump_progress, td, len_compressed); @@ -1662,7 +1811,6 @@ compress_core (gzFile file, char *inbuf, char *dest_buf, unsigned int len, error = EFAULT; break; } - inbuf += chunk_len; len -= chunk_len; maybe_yield(); }