Newer
Older
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
/* 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;
}
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,
{
int i;
struct list_head *t;
for (i = 0; i < info->numnote; i++)
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++)
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));
}
/* Free data possibly allocated by fill_files_note(): */
if (info->notes_files)
vfree(info->notes_files->data);
Denys Vlasenko
committed
kfree(info->prstatus);
kfree(info->psinfo);
kfree(info->notes);
kfree(info->fpu);
#ifdef ELF_CORE_COPY_XFPREGS
kfree(info->xfpu);
#endif
}
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
static struct vm_area_struct *first_vma(struct task_struct *tsk,
struct vm_area_struct *gate_vma)
{
struct vm_area_struct *ret = tsk->mm->mmap;
if (ret)
return ret;
return gate_vma;
}
/*
* Helper function for iterating across a vma list. It ensures that the caller
* will visit `gate_vma' prior to terminating the search.
*/
static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
struct vm_area_struct *gate_vma)
{
struct vm_area_struct *ret;
ret = this_vma->vm_next;
if (ret)
return ret;
if (this_vma == gate_vma)
return NULL;
return gate_vma;
}
static void fill_extnum_info(struct elfhdr *elf, struct elf_shdr *shdr4extnum,
elf_addr_t e_shoff, int segs)
{
elf->e_shoff = e_shoff;
elf->e_shentsize = sizeof(*shdr4extnum);
elf->e_shnum = 1;
elf->e_shstrndx = SHN_UNDEF;
memset(shdr4extnum, 0, sizeof(*shdr4extnum));
shdr4extnum->sh_type = SHT_NULL;
shdr4extnum->sh_size = elf->e_shnum;
shdr4extnum->sh_link = elf->e_shstrndx;
shdr4extnum->sh_info = segs;
}
/*
* Actual dumper
*
* This is a two-pass process; first we find the offsets of the bits,
* and then they are actually written out. If we run out of core limit
* we just truncate.
*/
static int elf_core_dump(struct coredump_params *cprm)
int segs, i;
size_t vma_data_size = 0;
struct vm_area_struct *vma, *gate_vma;
loff_t offset = 0, dataoff;
struct elf_note_info info = { };
Daisuke HATAYAMA
committed
struct elf_phdr *phdr4note = NULL;
struct elf_shdr *shdr4extnum = NULL;
Elf_Half e_phnum;
elf_addr_t e_shoff;
elf_addr_t *vma_filesz = NULL;
/*
* We no longer stop all VM operations.
*
* This is because those proceses that could possibly change map_count
* or the mmap / vma pages are now blocked in do_exit on current
* finishing this core dump.
*
* Only ptrace can touch these memory addresses, but it doesn't change
* the map_count or the pages allocated. So no possibility of crashing
* exists while dumping the mm->vm_next areas to the core file.
*/
/* alloc memory for large data structures: too large to be on stack */
elf = kmalloc(sizeof(*elf), GFP_KERNEL);
if (!elf)
/*
* The number of segs are recored into ELF header as 16bit value.
* Please check DEFAULT_MAX_MAP_COUNT definition when you modify here.
*/
segs += elf_core_extra_phdrs();
gate_vma = get_gate_vma(current->mm);
if (gate_vma != NULL)
segs++;
/* for notes section */
segs++;
/* If segs > PN_XNUM(0xffff), then e_phnum overflows. To avoid
* this, kernel supports extended numbering. Have a look at
* include/linux/elf.h for further information. */
e_phnum = segs > PN_XNUM ? PN_XNUM : segs;
* Collect all the non-memory information about the process for the
* notes. This also sets up the file header.
if (!fill_note_info(elf, e_phnum, &info, cprm->siginfo, cprm->regs))
fs = get_fs();
set_fs(KERNEL_DS);
offset += sizeof(*elf); /* Elf header */
offset += segs * sizeof(struct elf_phdr); /* Program headers */
sz += elf_coredump_extra_notes_size();
Daisuke HATAYAMA
committed
phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL);
if (!phdr4note)
goto end_coredump;
Daisuke HATAYAMA
committed
fill_elf_note_phdr(phdr4note, sz, offset);
offset += sz;
}
dataoff = offset = roundup(offset, ELF_EXEC_PAGESIZE);
vma_filesz = kmalloc_array(segs - 1, sizeof(*vma_filesz), GFP_KERNEL);
if (!vma_filesz)
goto end_coredump;
for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
vma = next_vma(vma, gate_vma)) {
unsigned long dump_size;
dump_size = vma_dump_size(vma, cprm->mm_flags);
vma_filesz[i++] = dump_size;
vma_data_size += dump_size;
}
offset += vma_data_size;
offset += elf_core_extra_data_size();
e_shoff = offset;
if (e_phnum == PN_XNUM) {
shdr4extnum = kmalloc(sizeof(*shdr4extnum), GFP_KERNEL);
if (!shdr4extnum)
goto end_coredump;
fill_extnum_info(elf, shdr4extnum, e_shoff, segs);
}
offset = dataoff;
Daisuke HATAYAMA
committed
goto end_coredump;
Daisuke HATAYAMA
committed
goto end_coredump;
for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
struct elf_phdr phdr;
phdr.p_type = PT_LOAD;
phdr.p_offset = offset;
phdr.p_vaddr = vma->vm_start;
phdr.p_paddr = 0;
phdr.p_filesz = vma_filesz[i++];
offset += phdr.p_filesz;
phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0;
if (vma->vm_flags & VM_WRITE)
phdr.p_flags |= PF_W;
if (vma->vm_flags & VM_EXEC)
phdr.p_flags |= PF_X;
goto end_coredump;
if (!elf_core_write_extra_phdrs(cprm, offset))
goto end_coredump;
if (elf_coredump_extra_notes_write(cprm))
goto end_coredump;
/* Align to page */
if (!dump_skip(cprm, dataoff - cprm->pos))
for (i = 0, vma = first_vma(current, gate_vma); vma != NULL;
end = vma->vm_start + vma_filesz[i++];
for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) {
struct page *page;
int stop;
page = get_dump_page(addr);
if (page) {
void *kaddr = kmap(page);
stop = !dump_emit(cprm, kaddr, PAGE_SIZE);
put_page(page);
stop = !dump_skip(cprm, PAGE_SIZE);
if (!elf_core_write_extra_data(cprm))
goto end_coredump;
if (e_phnum == PN_XNUM) {
if (!dump_emit(cprm, shdr4extnum, sizeof(*shdr4extnum)))
goto end_coredump;
}
kfree(vma_filesz);
Daisuke HATAYAMA
committed
kfree(phdr4note);