elf core dump: notes reorg

This pulls out the code for writing the notes segment of an ELF core dump
into separate functions.  This cleanly isolates into one cluster of
functions everything that deals with the note formats and the hooks into
arch code to fill them.  The top-level elf_core_dump function itself now
deals purely with the generic ELF format and the memory segments.

This only moves code around into functions that can be inlined away.
It should not change any behavior at all.

Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
Roland McGrath 2008-01-30 13:31:44 +01:00 committed by Ingo Molnar
parent bdf88217b7
commit 3aba481fc9
1 changed files with 194 additions and 130 deletions

View File

@ -1395,7 +1395,8 @@ static int writenote(struct memelfnote *men, struct file *file,
if (!dump_seek(file, (off))) \
goto end_coredump;
static void fill_elf_header(struct elfhdr *elf, int segs)
static void fill_elf_header(struct elfhdr *elf, int segs,
u16 machine, u32 flags, u8 osabi)
{
memcpy(elf->e_ident, ELFMAG, SELFMAG);
elf->e_ident[EI_CLASS] = ELF_CLASS;
@ -1405,12 +1406,12 @@ static void fill_elf_header(struct elfhdr *elf, int segs)
memset(elf->e_ident+EI_PAD, 0, EI_NIDENT-EI_PAD);
elf->e_type = ET_CORE;
elf->e_machine = ELF_ARCH;
elf->e_machine = machine;
elf->e_version = EV_CURRENT;
elf->e_entry = 0;
elf->e_phoff = sizeof(struct elfhdr);
elf->e_shoff = 0;
elf->e_flags = ELF_CORE_EFLAGS;
elf->e_flags = flags;
elf->e_ehsize = sizeof(struct elfhdr);
elf->e_phentsize = sizeof(struct elf_phdr);
elf->e_phnum = segs;
@ -1517,6 +1518,16 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p,
return 0;
}
static void fill_auxv_note(struct memelfnote *note, struct mm_struct *mm)
{
elf_addr_t *auxv = (elf_addr_t *) mm->saved_auxv;
int i = 0;
do
i += 2;
while (auxv[i - 2] != AT_NULL);
fill_note(note, "CORE", NT_AUXV, i * sizeof(elf_addr_t), auxv);
}
/* Here is the structure in which status of each thread is captured. */
struct elf_thread_status
{
@ -1569,6 +1580,174 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
return sz;
}
struct elf_note_info {
struct memelfnote *notes;
struct elf_prstatus *prstatus; /* NT_PRSTATUS */
struct elf_prpsinfo *psinfo; /* NT_PRPSINFO */
struct list_head thread_list;
elf_fpregset_t *fpu;
#ifdef ELF_CORE_COPY_XFPREGS
elf_fpxregset_t *xfpu;
#endif
int thread_status_size;
int numnote;
};
static int fill_note_info(struct elfhdr *elf, int phdrs,
struct elf_note_info *info,
long signr, struct pt_regs *regs)
{
#define NUM_NOTES 6
struct list_head *t;
struct task_struct *g, *p;
info->notes = NULL;
info->prstatus = NULL;
info->psinfo = NULL;
info->fpu = NULL;
#ifdef ELF_CORE_COPY_XFPREGS
info->xfpu = NULL;
#endif
INIT_LIST_HEAD(&info->thread_list);
info->notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote),
GFP_KERNEL);
if (!info->notes)
return 0;
info->psinfo = kmalloc(sizeof(*info->psinfo), GFP_KERNEL);
if (!info->psinfo)
return 0;
info->prstatus = kmalloc(sizeof(*info->prstatus), GFP_KERNEL);
if (!info->prstatus)
return 0;
info->fpu = kmalloc(sizeof(*info->fpu), GFP_KERNEL);
if (!info->fpu)
return 0;
#ifdef ELF_CORE_COPY_XFPREGS
info->xfpu = kmalloc(sizeof(*info->xfpu), GFP_KERNEL);
if (!info->xfpu)
return 0;
#endif
info->thread_status_size = 0;
if (signr) {
struct elf_thread_status *tmp;
rcu_read_lock();
do_each_thread(g, p)
if (current->mm == p->mm && current != p) {
tmp = kzalloc(sizeof(*tmp), GFP_ATOMIC);
if (!tmp) {
rcu_read_unlock();
return 0;
}
tmp->thread = p;
list_add(&tmp->list, &info->thread_list);
}
while_each_thread(g, p);
rcu_read_unlock();
list_for_each(t, &info->thread_list) {
struct elf_thread_status *tmp;
int sz;
tmp = list_entry(t, struct elf_thread_status, list);
sz = elf_dump_thread_status(signr, tmp);
info->thread_status_size += sz;
}
}
/* now collect the dump for the current */
memset(info->prstatus, 0, sizeof(*info->prstatus));
fill_prstatus(info->prstatus, current, signr);
elf_core_copy_regs(&info->prstatus->pr_reg, regs);
/* Set up header */
fill_elf_header(elf, phdrs, ELF_ARCH, ELF_CORE_EFLAGS, ELF_OSABI);
/*
* Set up the notes in similar form to SVR4 core dumps made
* with info from their /proc.
*/
fill_note(info->notes + 0, "CORE", NT_PRSTATUS,
sizeof(*info->prstatus), info->prstatus);
fill_psinfo(info->psinfo, current->group_leader, current->mm);
fill_note(info->notes + 1, "CORE", NT_PRPSINFO,
sizeof(*info->psinfo), info->psinfo);
info->numnote = 2;
fill_auxv_note(&info->notes[info->numnote++], current->mm);
/* Try to dump the FPU. */
info->prstatus->pr_fpvalid = elf_core_copy_task_fpregs(current, regs,
info->fpu);
if (info->prstatus->pr_fpvalid)
fill_note(info->notes + info->numnote++,
"CORE", NT_PRFPREG, sizeof(*info->fpu), info->fpu);
#ifdef ELF_CORE_COPY_XFPREGS
if (elf_core_copy_task_xfpregs(current, info->xfpu))
fill_note(info->notes + info->numnote++,
"LINUX", ELF_CORE_XFPREG_TYPE,
sizeof(*info->xfpu), info->xfpu);
#endif
return 1;
#undef NUM_NOTES
}
static size_t get_note_info_size(struct elf_note_info *info)
{
int sz = 0;
int i;
for (i = 0; i < info->numnote; i++)
sz += notesize(info->notes + i);
sz += info->thread_status_size;
return sz;
}
static int write_note_info(struct elf_note_info *info,
struct file *file, loff_t *foffset)
{
int i;
struct list_head *t;
for (i = 0; i < info->numnote; i++)
if (!writenote(info->notes + i, file, foffset))
return 0;
/* write out the thread status notes section */
list_for_each(t, &info->thread_list) {
struct elf_thread_status *tmp =
list_entry(t, struct elf_thread_status, list);
for (i = 0; i < tmp->num_notes; i++)
if (!writenote(&tmp->notes[i], file, foffset))
return 0;
}
return 1;
}
static void free_note_info(struct elf_note_info *info)
{
while (!list_empty(&info->thread_list)) {
struct list_head *tmp = info->thread_list.next;
list_del(tmp);
kfree(list_entry(tmp, struct elf_thread_status, list));
}
kfree(info->prstatus);
kfree(info->psinfo);
kfree(info->notes);
kfree(info->fpu);
#ifdef ELF_CORE_COPY_XFPREGS
kfree(info->xfpu);
#endif
}
static struct vm_area_struct *first_vma(struct task_struct *tsk,
struct vm_area_struct *gate_vma)
{
@ -1604,29 +1783,15 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
*/
static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
{
#define NUM_NOTES 6
int has_dumped = 0;
mm_segment_t fs;
int segs;
size_t size = 0;
int i;
struct vm_area_struct *vma, *gate_vma;
struct elfhdr *elf = NULL;
loff_t offset = 0, dataoff, foffset;
int numnote;
struct memelfnote *notes = NULL;
struct elf_prstatus *prstatus = NULL; /* NT_PRSTATUS */
struct elf_prpsinfo *psinfo = NULL; /* NT_PRPSINFO */
struct task_struct *g, *p;
LIST_HEAD(thread_list);
struct list_head *t;
elf_fpregset_t *fpu = NULL;
#ifdef ELF_CORE_COPY_XFPREGS
elf_fpxregset_t *xfpu = NULL;
#endif
int thread_status_size = 0;
elf_addr_t *auxv;
unsigned long mm_flags;
struct elf_note_info info;
/*
* We no longer stop all VM operations.
@ -1644,52 +1809,6 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
elf = kmalloc(sizeof(*elf), GFP_KERNEL);
if (!elf)
goto cleanup;
prstatus = kmalloc(sizeof(*prstatus), GFP_KERNEL);
if (!prstatus)
goto cleanup;
psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL);
if (!psinfo)
goto cleanup;
notes = kmalloc(NUM_NOTES * sizeof(struct memelfnote), GFP_KERNEL);
if (!notes)
goto cleanup;
fpu = kmalloc(sizeof(*fpu), GFP_KERNEL);
if (!fpu)
goto cleanup;
#ifdef ELF_CORE_COPY_XFPREGS
xfpu = kmalloc(sizeof(*xfpu), GFP_KERNEL);
if (!xfpu)
goto cleanup;
#endif
if (signr) {
struct elf_thread_status *tmp;
rcu_read_lock();
do_each_thread(g,p)
if (current->mm == p->mm && current != p) {
tmp = kzalloc(sizeof(*tmp), GFP_ATOMIC);
if (!tmp) {
rcu_read_unlock();
goto cleanup;
}
tmp->thread = p;
list_add(&tmp->list, &thread_list);
}
while_each_thread(g,p);
rcu_read_unlock();
list_for_each(t, &thread_list) {
struct elf_thread_status *tmp;
int sz;
tmp = list_entry(t, struct elf_thread_status, list);
sz = elf_dump_thread_status(signr, tmp);
thread_status_size += sz;
}
}
/* now collect the dump for the current */
memset(prstatus, 0, sizeof(*prstatus));
fill_prstatus(prstatus, current, signr);
elf_core_copy_regs(&prstatus->pr_reg, regs);
segs = current->mm->map_count;
#ifdef ELF_CORE_EXTRA_PHDRS
@ -1700,42 +1819,16 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
if (gate_vma != NULL)
segs++;
/* Set up header */
fill_elf_header(elf, segs + 1); /* including notes section */
/*
* Collect all the non-memory information about the process for the
* notes. This also sets up the file header.
*/
if (!fill_note_info(elf, segs + 1, /* including notes section */
&info, signr, regs))
goto cleanup;
has_dumped = 1;
current->flags |= PF_DUMPCORE;
/*
* Set up the notes in similar form to SVR4 core dumps made
* with info from their /proc.
*/
fill_note(notes + 0, "CORE", NT_PRSTATUS, sizeof(*prstatus), prstatus);
fill_psinfo(psinfo, current->group_leader, current->mm);
fill_note(notes + 1, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo);
numnote = 2;
auxv = (elf_addr_t *)current->mm->saved_auxv;
i = 0;
do
i += 2;
while (auxv[i - 2] != AT_NULL);
fill_note(&notes[numnote++], "CORE", NT_AUXV,
i * sizeof(elf_addr_t), auxv);
/* Try to dump the FPU. */
if ((prstatus->pr_fpvalid =
elf_core_copy_task_fpregs(current, regs, fpu)))
fill_note(notes + numnote++,
"CORE", NT_PRFPREG, sizeof(*fpu), fpu);
#ifdef ELF_CORE_COPY_XFPREGS
if (elf_core_copy_task_xfpregs(current, xfpu))
fill_note(notes + numnote++,
"LINUX", ELF_CORE_XFPREG_TYPE, sizeof(*xfpu), xfpu);
#endif
fs = get_fs();
set_fs(KERNEL_DS);
@ -1748,12 +1841,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
/* Write notes phdr entry */
{
struct elf_phdr phdr;
int sz = 0;
for (i = 0; i < numnote; i++)
sz += notesize(notes + i);
sz += thread_status_size;
size_t sz = get_note_info_size(&info);
sz += elf_coredump_extra_notes_size();
@ -1798,23 +1886,12 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
#endif
/* write out the notes section */
for (i = 0; i < numnote; i++)
if (!writenote(notes + i, file, &foffset))
goto end_coredump;
if (!write_note_info(&info, file, &foffset))
goto end_coredump;
if (elf_coredump_extra_notes_write(file, &foffset))
goto end_coredump;
/* write out the thread status notes section */
list_for_each(t, &thread_list) {
struct elf_thread_status *tmp =
list_entry(t, struct elf_thread_status, list);
for (i = 0; i < tmp->num_notes; i++)
if (!writenote(&tmp->notes[i], file, &foffset))
goto end_coredump;
}
/* Align to page */
DUMP_SEEK(dataoff - foffset);
@ -1865,22 +1942,9 @@ end_coredump:
set_fs(fs);
cleanup:
while (!list_empty(&thread_list)) {
struct list_head *tmp = thread_list.next;
list_del(tmp);
kfree(list_entry(tmp, struct elf_thread_status, list));
}
kfree(elf);
kfree(prstatus);
kfree(psinfo);
kfree(notes);
kfree(fpu);
#ifdef ELF_CORE_COPY_XFPREGS
kfree(xfpu);
#endif
free_note_info(&info);
return has_dumped;
#undef NUM_NOTES
}
#endif /* USE_ELF_CORE_DUMP */