Re-factor coredump routines. For each type of notes an output
function is provided, which is used either to calculate the note size or output it to sbuf. On the first pass the notes are registered in a list and the resulting size is found, on the second pass the list is traversed outputing notes to sbuf. For the sbuf a drain routine is provided that writes data to a core file. The main goal of the change is to make coredump to write notes directly to the core file, without preliminary preparing them all in a memory buffer. Storing notes in memory is not a problem for the current, rather small, set of notes we write to the core, but it may becomes an issue when we start to store procstat notes. Reviewed by: jhb (initial version), kib Discussed with: jhb, kib MFC after: 3 weeks
This commit is contained in:
parent
8fa3a54014
commit
bd3902134c
@ -53,6 +53,7 @@ __FBSDID("$FreeBSD$");
|
||||
#include <sys/racct.h>
|
||||
#include <sys/resourcevar.h>
|
||||
#include <sys/rwlock.h>
|
||||
#include <sys/sbuf.h>
|
||||
#include <sys/sf_buf.h>
|
||||
#include <sys/smp.h>
|
||||
#include <sys/systm.h>
|
||||
@ -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 <compat/freebsd32/freebsd32.h>
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user