X-Git-Url: https://err.no/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=fs%2Fbinfmt_elf.c;h=4628c42ca892ef9248dc7a6dc0f1ed2134211de6;hb=19e66a67e9b25874cd5e184e7d381ce1b955df11;hp=ba24cb2ff6ceb83859f782677372ded1c69b20c2;hpb=a1b59e802f846b6b0e057507386068fcc6dff442;p=linux-2.6 diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index ba24cb2ff6..4628c42ca8 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -45,14 +45,15 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs); static int load_elf_library(struct file *); -static unsigned long elf_map (struct file *, unsigned long, struct elf_phdr *, int, int, unsigned long); +static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *, + int, int, unsigned long); /* * If we don't support core dumping, then supply a NULL so we * don't even try. */ #if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE) -static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file); +static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit); #else #define elf_core_dump NULL #endif @@ -80,7 +81,7 @@ static struct linux_binfmt elf_format = { .hasvdso = 1 }; -#define BAD_ADDR(x) IS_ERR_VALUE(x) +#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE) static int set_brk(unsigned long start, unsigned long end) { @@ -116,7 +117,7 @@ static int padzero(unsigned long elf_bss) return 0; } -/* Let's use some macros to make this stack manipulation a litle clearer */ +/* Let's use some macros to make this stack manipulation a little clearer */ #ifdef CONFIG_STACK_GROWSUP #define STACK_ADD(sp, items) ((elf_addr_t __user *)(sp) + (items)) #define STACK_ROUND(sp, items) \ @@ -150,6 +151,14 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, struct task_struct *tsk = current; struct vm_area_struct *vma; + /* + * In some cases (e.g. Hyper-Threading), we want to avoid L1 + * evictions by the processes running on the same package. One + * thing we can do is to shuffle the initial stack for them. + */ + + p = arch_align_stack(p); + /* * If this architecture has a platform capability string, copy it * to userspace. In some cases (Sparc), this info is impossible @@ -160,14 +169,6 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, if (k_platform) { size_t len = strlen(k_platform) + 1; - /* - * In some cases (e.g. Hyper-Threading), we want to avoid L1 - * evictions by the processes running on the same package. One - * thing we can do is to shuffle the initial stack for them. - */ - - p = arch_align_stack(p); - u_platform = (elf_addr_t __user *)STACK_ALLOC(p, len); if (__copy_to_user(u_platform, k_platform, len)) return -EFAULT; @@ -175,6 +176,7 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, /* Create the ELF interpreter info */ elf_info = (elf_addr_t *)current->mm->saved_auxv; + /* update AT_VECTOR_SIZE_BASE if the number of NEW_AUX_ENT() changes */ #define NEW_AUX_ENT(id, val) \ do { \ elf_info[ei_index++] = id; \ @@ -185,6 +187,8 @@ create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec, /* * ARCH_DLINFO must come first so PPC can do its special alignment of * AUXV. + * update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT() in + * ARCH_DLINFO changes */ ARCH_DLINFO; #endif @@ -432,7 +436,7 @@ static unsigned long load_elf_interp(struct elfhdr *interp_elf_ex, load_addr = -vaddr; map_addr = elf_map(interpreter, load_addr + vaddr, - eppnt, elf_prot, elf_type, total_size); + eppnt, elf_prot, elf_type, total_size); total_size = 0; if (!*interp_map_addr) *interp_map_addr = map_addr; @@ -591,7 +595,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) int load_addr_set = 0; char * elf_interpreter = NULL; unsigned int interpreter_type = INTERPRETER_NONE; - unsigned char ibcs2_interpreter = 0; unsigned long error; struct elf_phdr *elf_ppnt, *elf_phdata; unsigned long elf_bss, elf_brk; @@ -709,14 +712,6 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) if (elf_interpreter[elf_ppnt->p_filesz - 1] != '\0') goto out_free_interp; - /* If the program interpreter is one of these two, - * then assume an iBCS2 image. Otherwise assume - * a native linux image. - */ - if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 || - strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) - ibcs2_interpreter = 1; - /* * The early SET_PERSONALITY here is so that the lookup * for the interpreter happens in the namespace of the @@ -736,7 +731,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) * switch really is going to happen - do this in * flush_thread(). - akpm */ - SET_PERSONALITY(loc->elf_ex, ibcs2_interpreter); + SET_PERSONALITY(loc->elf_ex, 0); interpreter = open_exec(elf_interpreter); retval = PTR_ERR(interpreter); @@ -779,6 +774,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) /* Some simple consistency checks for the interpreter */ if (elf_interpreter) { + static int warn; interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; /* Now figure out which format our binary is */ @@ -790,6 +786,13 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) if (memcmp(loc->interp_elf_ex.e_ident, ELFMAG, SELFMAG) != 0) interpreter_type &= ~INTERPRETER_ELF; + if (interpreter_type == INTERPRETER_AOUT && warn < 10) { + printk(KERN_WARNING "a.out ELF interpreter %s is " + "deprecated and will not be supported " + "after Linux 2.6.25\n", elf_interpreter); + warn++; + } + retval = -ELIBBAD; if (!interpreter_type) goto out_free_dentry; @@ -807,7 +810,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) goto out_free_dentry; } else { /* Executables without an interpreter also need a personality */ - SET_PERSONALITY(loc->elf_ex, ibcs2_interpreter); + SET_PERSONALITY(loc->elf_ex, 0); } /* OK, we are done with that, now set up the arg stuff, @@ -841,7 +844,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) /* Do this immediately, since STACK_TOP as used in setup_arg_pages may depend on the personality. */ - SET_PERSONALITY(loc->elf_ex, ibcs2_interpreter); + SET_PERSONALITY(loc->elf_ex, 0); if (elf_read_implies_exec(loc->elf_ex, executable_stack)) current->personality |= READ_IMPLIES_EXEC; @@ -925,7 +928,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) } error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, - elf_prot, elf_flags,0); + elf_prot, elf_flags, 0); if (BAD_ADDR(error)) { send_sig(SIGKILL, current, 0); retval = IS_ERR((void *)error) ? @@ -1011,7 +1014,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) interpreter, &interp_map_addr, load_bias); - if (!BAD_ADDR(elf_entry)) { + if (!IS_ERR((void *)elf_entry)) { /* * load_elf_interp() returns relocation * adjustment @@ -1073,6 +1076,12 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs) current->mm->end_data = end_data; current->mm->start_stack = bprm->p; +#ifdef arch_randomize_brk + if (current->flags & PF_RANDOMIZE) + current->mm->brk = current->mm->start_brk = + arch_randomize_brk(current->mm); +#endif + if (current->personality & MMAP_PAGE_ZERO) { /* Why this, you ask??? Well SVr4 maps page 0 as read-only, and some applications "depend" upon this behavior. @@ -1256,35 +1265,68 @@ static int dump_seek(struct file *file, loff_t off) } /* - * Decide whether a segment is worth dumping; default is yes to be - * sure (missing info is worse than too much; etc). - * Personally I'd include everything, and use the coredump limit... - * - * I think we should skip something. But I am not sure how. H.J. + * Decide what to dump of a segment, part, all or none. */ -static int maydump(struct vm_area_struct *vma, unsigned long mm_flags) +static unsigned long vma_dump_size(struct vm_area_struct *vma, + unsigned long mm_flags) { /* The vma can be set up to tell us the answer directly. */ if (vma->vm_flags & VM_ALWAYSDUMP) - return 1; + goto whole; /* Do not dump I/O mapped devices or special mappings */ if (vma->vm_flags & (VM_IO | VM_RESERVED)) return 0; +#define FILTER(type) (mm_flags & (1UL << MMF_DUMP_##type)) + /* By default, dump shared memory if mapped from an anonymous file. */ if (vma->vm_flags & VM_SHARED) { - if (vma->vm_file->f_path.dentry->d_inode->i_nlink == 0) - return test_bit(MMF_DUMP_ANON_SHARED, &mm_flags); - else - return test_bit(MMF_DUMP_MAPPED_SHARED, &mm_flags); + if (vma->vm_file->f_path.dentry->d_inode->i_nlink == 0 ? + FILTER(ANON_SHARED) : FILTER(MAPPED_SHARED)) + goto whole; + return 0; } - /* By default, if it hasn't been written to, don't write it out. */ - if (!vma->anon_vma) - return test_bit(MMF_DUMP_MAPPED_PRIVATE, &mm_flags); + /* Dump segments that have been written to. */ + if (vma->anon_vma && FILTER(ANON_PRIVATE)) + goto whole; + if (vma->vm_file == NULL) + return 0; + + if (FILTER(MAPPED_PRIVATE)) + goto whole; + + /* + * If this looks like the beginning of a DSO or executable mapping, + * check for an ELF header. If we find one, dump the first page to + * aid in determining what was mapped here. + */ + if (FILTER(ELF_HEADERS) && vma->vm_file != NULL && vma->vm_pgoff == 0) { + u32 __user *header = (u32 __user *) vma->vm_start; + u32 word; + /* + * Doing it this way gets the constant folded by GCC. + */ + union { + u32 cmp; + char elfmag[SELFMAG]; + } magic; + BUILD_BUG_ON(SELFMAG != sizeof word); + magic.elfmag[EI_MAG0] = ELFMAG0; + magic.elfmag[EI_MAG1] = ELFMAG1; + magic.elfmag[EI_MAG2] = ELFMAG2; + magic.elfmag[EI_MAG3] = ELFMAG3; + if (get_user(word, header) == 0 && word == magic.cmp) + return PAGE_SIZE; + } + +#undef FILTER - return test_bit(MMF_DUMP_ANON_PRIVATE, &mm_flags); + return 0; + +whole: + return vma->vm_end - vma->vm_start; } /* An ELF note in memory */ @@ -1344,7 +1386,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; @@ -1354,12 +1397,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; @@ -1402,10 +1445,10 @@ static void fill_prstatus(struct elf_prstatus *prstatus, prstatus->pr_info.si_signo = prstatus->pr_cursig = signr; prstatus->pr_sigpend = p->pending.signal.sig[0]; prstatus->pr_sighold = p->blocked.sig[0]; - prstatus->pr_pid = p->pid; - prstatus->pr_ppid = p->parent->pid; - prstatus->pr_pgrp = process_group(p); - prstatus->pr_sid = process_session(p); + prstatus->pr_pid = task_pid_vnr(p); + prstatus->pr_ppid = task_pid_vnr(p->real_parent); + prstatus->pr_pgrp = task_pgrp_vnr(p); + prstatus->pr_sid = task_session_vnr(p); if (thread_group_leader(p)) { /* * This is the record for the group leader. Add in the @@ -1448,10 +1491,10 @@ static int fill_psinfo(struct elf_prpsinfo *psinfo, struct task_struct *p, psinfo->pr_psargs[i] = ' '; psinfo->pr_psargs[len] = 0; - psinfo->pr_pid = p->pid; - psinfo->pr_ppid = p->parent->pid; - psinfo->pr_pgrp = process_group(p); - psinfo->pr_sid = process_session(p); + psinfo->pr_pid = task_pid_vnr(p); + psinfo->pr_ppid = task_pid_vnr(p->real_parent); + psinfo->pr_pgrp = task_pgrp_vnr(p); + psinfo->pr_sid = task_session_vnr(p); i = p->state ? ffz(~p->state) + 1 : 0; psinfo->pr_state = i; @@ -1466,6 +1509,238 @@ 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); +} + +#ifdef CORE_DUMP_USE_REGSET +#include + +struct elf_thread_core_info { + struct elf_thread_core_info *next; + struct task_struct *task; + struct elf_prstatus prstatus; + struct memelfnote notes[0]; +}; + +struct elf_note_info { + struct elf_thread_core_info *thread; + struct memelfnote psinfo; + struct memelfnote auxv; + size_t size; + int thread_notes; +}; + +static int fill_thread_core_info(struct elf_thread_core_info *t, + const struct user_regset_view *view, + long signr, size_t *total) +{ + unsigned int i; + + /* + * NT_PRSTATUS is the one special case, because the regset data + * goes into the pr_reg field inside the note contents, rather + * than being the whole note contents. We fill the reset in here. + * We assume that regset 0 is NT_PRSTATUS. + */ + fill_prstatus(&t->prstatus, t->task, signr); + (void) view->regsets[0].get(t->task, &view->regsets[0], + 0, sizeof(t->prstatus.pr_reg), + &t->prstatus.pr_reg, NULL); + + fill_note(&t->notes[0], "CORE", NT_PRSTATUS, + sizeof(t->prstatus), &t->prstatus); + *total += notesize(&t->notes[0]); + + /* + * Each other regset might generate a note too. For each regset + * that has no core_note_type or is inactive, we leave t->notes[i] + * all zero and we'll know to skip writing it later. + */ + for (i = 1; i < view->n; ++i) { + const struct user_regset *regset = &view->regsets[i]; + if (regset->core_note_type && + (!regset->active || regset->active(t->task, regset))) { + int ret; + size_t size = regset->n * regset->size; + void *data = kmalloc(size, GFP_KERNEL); + if (unlikely(!data)) + return 0; + ret = regset->get(t->task, regset, + 0, size, data, NULL); + if (unlikely(ret)) + kfree(data); + else { + if (regset->core_note_type != NT_PRFPREG) + fill_note(&t->notes[i], "LINUX", + regset->core_note_type, + size, data); + else { + t->prstatus.pr_fpvalid = 1; + fill_note(&t->notes[i], "CORE", + NT_PRFPREG, size, data); + } + *total += notesize(&t->notes[i]); + } + } + } + + return 1; +} + +static int fill_note_info(struct elfhdr *elf, int phdrs, + struct elf_note_info *info, + long signr, struct pt_regs *regs) +{ + struct task_struct *dump_task = current; + const struct user_regset_view *view = task_user_regset_view(dump_task); + struct elf_thread_core_info *t; + struct elf_prpsinfo *psinfo; + struct task_struct *g, *p; + unsigned int i; + + info->size = 0; + info->thread = NULL; + + psinfo = kmalloc(sizeof(*psinfo), GFP_KERNEL); + fill_note(&info->psinfo, "CORE", NT_PRPSINFO, sizeof(*psinfo), psinfo); + + if (psinfo == NULL) + return 0; + + /* + * Figure out how many notes we're going to need for each thread. + */ + info->thread_notes = 0; + for (i = 0; i < view->n; ++i) + if (view->regsets[i].core_note_type != 0) + ++info->thread_notes; + + /* + * Sanity check. We rely on regset 0 being in NT_PRSTATUS, + * since it is our one special case. + */ + if (unlikely(info->thread_notes == 0) || + unlikely(view->regsets[0].core_note_type != NT_PRSTATUS)) { + WARN_ON(1); + return 0; + } + + /* + * Initialize the ELF file header. + */ + fill_elf_header(elf, phdrs, + view->e_machine, view->e_flags, view->ei_osabi); + + /* + * Allocate a structure for each thread. + */ + rcu_read_lock(); + do_each_thread(g, p) + if (p->mm == dump_task->mm) { + t = kzalloc(offsetof(struct elf_thread_core_info, + notes[info->thread_notes]), + GFP_ATOMIC); + if (unlikely(!t)) { + rcu_read_unlock(); + return 0; + } + t->task = p; + if (p == dump_task || !info->thread) { + t->next = info->thread; + info->thread = t; + } else { + /* + * Make sure to keep the original task at + * the head of the list. + */ + t->next = info->thread->next; + info->thread->next = t; + } + } + while_each_thread(g, p); + rcu_read_unlock(); + + /* + * Now fill in each thread's information. + */ + for (t = info->thread; t != NULL; t = t->next) + if (!fill_thread_core_info(t, view, signr, &info->size)) + return 0; + + /* + * Fill in the two process-wide notes. + */ + fill_psinfo(psinfo, dump_task->group_leader, dump_task->mm); + info->size += notesize(&info->psinfo); + + fill_auxv_note(&info->auxv, current->mm); + info->size += notesize(&info->auxv); + + return 1; +} + +static size_t get_note_info_size(struct elf_note_info *info) +{ + return info->size; +} + +/* + * Write all the notes for each thread. When writing the first thread, the + * process-wide notes are interleaved after the first thread-specific note. + */ +static int write_note_info(struct elf_note_info *info, + struct file *file, loff_t *foffset) +{ + bool first = 1; + struct elf_thread_core_info *t = info->thread; + + do { + int i; + + if (!writenote(&t->notes[0], file, foffset)) + return 0; + + if (first && !writenote(&info->psinfo, file, foffset)) + return 0; + if (first && !writenote(&info->auxv, file, foffset)) + return 0; + + for (i = 1; i < info->thread_notes; ++i) + if (t->notes[i].data && + !writenote(&t->notes[i], file, foffset)) + return 0; + + first = 0; + t = t->next; + } while (t); + + return 1; +} + +static void free_note_info(struct elf_note_info *info) +{ + struct elf_thread_core_info *threads = info->thread; + while (threads) { + unsigned int i; + struct elf_thread_core_info *t = threads; + threads = t->next; + WARN_ON(t->notes[0].data && t->notes[0].data != &t->prstatus); + for (i = 1; i < info->thread_notes; ++i) + kfree(t->notes[i].data); + kfree(t); + } + kfree(info->psinfo.data); +} + +#else + /* Here is the structure in which status of each thread is captured. */ struct elf_thread_status { @@ -1474,7 +1749,7 @@ struct elf_thread_status elf_fpregset_t fpu; /* NT_PRFPREG */ struct task_struct *thread; #ifdef ELF_CORE_COPY_XFPREGS - elf_fpxregset_t xfpu; /* NT_PRXFPREG */ + elf_fpxregset_t xfpu; /* ELF_CORE_XFPREG_TYPE */ #endif struct memelfnote notes[3]; int num_notes; @@ -1509,8 +1784,8 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) #ifdef ELF_CORE_COPY_XFPREGS if (elf_core_copy_task_xfpregs(p, &t->xfpu)) { - fill_note(&t->notes[2], "LINUX", NT_PRXFPREG, sizeof(t->xfpu), - &t->xfpu); + fill_note(&t->notes[2], "LINUX", ELF_CORE_XFPREG_TYPE, + sizeof(t->xfpu), &t->xfpu); t->num_notes++; sz += notesize(&t->notes[2]); } @@ -1518,6 +1793,176 @@ 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 +} + +#endif + static struct vm_area_struct *first_vma(struct task_struct *tsk, struct vm_area_struct *gate_vma) { @@ -1551,35 +1996,17 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma, * and then they are actually written out. If we run out of core limit * we just truncate. */ -static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) +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; - unsigned long limit = current->signal->rlim[RLIMIT_CORE].rlim_cur; - 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; -#ifdef ELF_CORE_WRITE_EXTRA_NOTES - int extra_notes_size; -#endif + struct elf_note_info info; /* * We no longer stop all VM operations. @@ -1597,52 +2024,6 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) 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 @@ -1653,42 +2034,16 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) if (gate_vma != NULL) segs++; - /* Set up header */ - fill_elf_header(elf, segs + 1); /* including notes section */ - - has_dumped = 1; - current->flags |= PF_DUMPCORE; - /* - * Set up the notes in similar form to SVR4 core dumps made - * with info from their /proc. + * 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; - 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(¬es[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", NT_PRXFPREG, sizeof(*xfpu), xfpu); -#endif + has_dumped = 1; + current->flags |= PF_DUMPCORE; fs = get_fs(); set_fs(KERNEL_DS); @@ -1701,17 +2056,9 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) /* Write notes phdr entry */ { struct elf_phdr phdr; - int sz = 0; + size_t sz = get_note_info_size(&info); - for (i = 0; i < numnote; i++) - sz += notesize(notes + i); - - sz += thread_status_size; - -#ifdef ELF_CORE_WRITE_EXTRA_NOTES - extra_notes_size = ELF_CORE_EXTRA_NOTES_SIZE; - sz += extra_notes_size; -#endif + sz += elf_coredump_extra_notes_size(); fill_elf_note_phdr(&phdr, sz, offset); offset += sz; @@ -1731,16 +2078,13 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) for (vma = first_vma(current, gate_vma); vma != NULL; vma = next_vma(vma, gate_vma)) { struct elf_phdr phdr; - size_t sz; - - sz = vma->vm_end - vma->vm_start; phdr.p_type = PT_LOAD; phdr.p_offset = offset; phdr.p_vaddr = vma->vm_start; phdr.p_paddr = 0; - phdr.p_filesz = maydump(vma, mm_flags) ? sz : 0; - phdr.p_memsz = sz; + phdr.p_filesz = vma_dump_size(vma, mm_flags); + phdr.p_memsz = vma->vm_end - vma->vm_start; offset += phdr.p_filesz; phdr.p_flags = vma->vm_flags & VM_READ ? PF_R : 0; if (vma->vm_flags & VM_WRITE) @@ -1757,24 +2101,11 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) #endif /* write out the notes section */ - for (i = 0; i < numnote; i++) - if (!writenote(notes + i, file, &foffset)) - goto end_coredump; - -#ifdef ELF_CORE_WRITE_EXTRA_NOTES - ELF_CORE_WRITE_EXTRA_NOTES; - foffset += extra_notes_size; -#endif - - /* 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); + if (!write_note_info(&info, file, &foffset)) + goto end_coredump; - for (i = 0; i < tmp->num_notes; i++) - if (!writenote(&tmp->notes[i], file, &foffset)) - goto end_coredump; - } + if (elf_coredump_extra_notes_write(file, &foffset)) + goto end_coredump; /* Align to page */ DUMP_SEEK(dataoff - foffset); @@ -1782,13 +2113,11 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) for (vma = first_vma(current, gate_vma); vma != NULL; vma = next_vma(vma, gate_vma)) { unsigned long addr; + unsigned long end; - if (!maydump(vma, mm_flags)) - continue; + end = vma->vm_start + vma_dump_size(vma, mm_flags); - for (addr = vma->vm_start; - addr < vma->vm_end; - addr += PAGE_SIZE) { + for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) { struct page *page; struct vm_area_struct *vma; @@ -1796,7 +2125,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file) &page, &vma) <= 0) { DUMP_SEEK(PAGE_SIZE); } else { - if (page == ZERO_PAGE(addr)) { + if (page == ZERO_PAGE(0)) { if (!dump_seek(file, PAGE_SIZE)) { page_cache_release(page); goto end_coredump; @@ -1828,22 +2157,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 */