diff options
Diffstat (limited to 'fs/binfmt_elf.c')
-rw-r--r-- | fs/binfmt_elf.c | 326 |
1 files changed, 181 insertions, 145 deletions
diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c index 47345a358..d03873a3b 100644 --- a/fs/binfmt_elf.c +++ b/fs/binfmt_elf.c @@ -21,6 +21,7 @@ #include <linux/signal.h> #include <linux/binfmts.h> #include <linux/string.h> +#include <linux/file.h> #include <linux/fcntl.h> #include <linux/ptrace.h> #include <linux/malloc.h> @@ -34,7 +35,7 @@ #include <linux/config.h> -#define DLINFO_ITEMS 12 +#define DLINFO_ITEMS 13 #include <linux/elf.h> @@ -135,15 +136,35 @@ create_elf_tables(char *p, int argc, int envc, elf_caddr_t *argv; elf_caddr_t *envp; elf_addr_t *sp, *csp; + char *k_platform, *u_platform; + long hwcap; + size_t platform_len = 0; + + /* + * Get hold of platform and hardware capabilities masks for + * the machine we are running on. In some cases (Sparc), + * this info is impossible to get, in others (i386) it is + * merely difficult. + */ + + hwcap = ELF_HWCAP; + k_platform = ELF_PLATFORM; + + if (k_platform) { + platform_len = strlen(k_platform) + 1; + u_platform = p - platform_len; + __copy_to_user(u_platform, k_platform, platform_len); + } else + u_platform = p; /* * Force 16 byte _final_ alignment here for generality. * Leave an extra 16 bytes free so that on the PowerPC we * can move the aux table up to start on a 16-byte boundary. */ - sp = (elf_addr_t *) ((~15UL & (unsigned long) p) - 16UL); + sp = (elf_addr_t *)((~15UL & (unsigned long)(u_platform)) - 16UL); csp = sp; - csp -= exec ? DLINFO_ITEMS*2 : 2; + csp -= ((exec ? DLINFO_ITEMS*2 : 4) + (k_platform ? 2 : 0)); csp -= envc+1; csp -= argc+1; csp -= (!ibcs ? 3 : 1); /* argc itself */ @@ -160,21 +181,27 @@ create_elf_tables(char *p, int argc, int envc, sp -= 2; NEW_AUX_ENT(0, AT_NULL, 0); + if (k_platform) { + sp -= 2; + NEW_AUX_ENT(0, AT_PLATFORM, (elf_addr_t)(unsigned long) u_platform); + } + sp -= 2; + NEW_AUX_ENT(0, AT_HWCAP, hwcap); if (exec) { sp -= 11*2; - NEW_AUX_ENT (0, AT_PHDR, load_addr + exec->e_phoff); - NEW_AUX_ENT (1, AT_PHENT, sizeof (struct elf_phdr)); - NEW_AUX_ENT (2, AT_PHNUM, exec->e_phnum); - NEW_AUX_ENT (3, AT_PAGESZ, ELF_EXEC_PAGESIZE); - NEW_AUX_ENT (4, AT_BASE, interp_load_addr); - NEW_AUX_ENT (5, AT_FLAGS, 0); - NEW_AUX_ENT (6, AT_ENTRY, (elf_addr_t) exec->e_entry); - NEW_AUX_ENT (7, AT_UID, (elf_addr_t) current->uid); - NEW_AUX_ENT (8, AT_EUID, (elf_addr_t) current->euid); - NEW_AUX_ENT (9, AT_GID, (elf_addr_t) current->gid); - NEW_AUX_ENT (10, AT_EGID, (elf_addr_t) current->egid); + NEW_AUX_ENT(0, AT_PHDR, load_addr + exec->e_phoff); + NEW_AUX_ENT(1, AT_PHENT, sizeof (struct elf_phdr)); + NEW_AUX_ENT(2, AT_PHNUM, exec->e_phnum); + NEW_AUX_ENT(3, AT_PAGESZ, ELF_EXEC_PAGESIZE); + NEW_AUX_ENT(4, AT_BASE, interp_load_addr); + NEW_AUX_ENT(5, AT_FLAGS, 0); + NEW_AUX_ENT(6, AT_ENTRY, (elf_addr_t) exec->e_entry); + NEW_AUX_ENT(7, AT_UID, (elf_addr_t) current->uid); + NEW_AUX_ENT(8, AT_EUID, (elf_addr_t) current->euid); + NEW_AUX_ENT(9, AT_GID, (elf_addr_t) current->gid); + NEW_AUX_ENT(10, AT_EGID, (elf_addr_t) current->egid); } #undef NEW_AUX_ENT @@ -356,53 +383,61 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, /* Map the last of the bss segment */ if (last_bss > elf_bss) - do_mmap(NULL, elf_bss, last_bss-elf_bss, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - kfree(elf_phdata); + do_mmap(NULL, elf_bss, last_bss - elf_bss, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); *interp_load_addr = load_addr; - return ((unsigned long) interp_elf_ex->e_entry) + load_addr; + error = ((unsigned long) interp_elf_ex->e_entry) + load_addr; + +out_close: + fput(file); + sys_close(elf_exec_fileno); +out_free: + kfree(elf_phdata); +out: + return error; } static unsigned long load_aout_interp(struct exec * interp_ex, struct dentry * interpreter_dentry) { - int retval; - unsigned long elf_entry; - - current->mm->brk = interp_ex->a_bss + - (current->mm->end_data = interp_ex->a_data + - (current->mm->end_code = interp_ex->a_text)); - elf_entry = interp_ex->a_entry; - - - if (N_MAGIC(*interp_ex) == OMAGIC) { - do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - retval = read_exec(interpreter_dentry, 32, (char *) 0, - interp_ex->a_text+interp_ex->a_data, 0); - } else if (N_MAGIC(*interp_ex) == ZMAGIC || N_MAGIC(*interp_ex) == QMAGIC) { - do_mmap(NULL, 0, interp_ex->a_text+interp_ex->a_data, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - retval = read_exec(interpreter_dentry, - N_TXTOFF(*interp_ex) , - (char *) N_TXTADDR(*interp_ex), - interp_ex->a_text+interp_ex->a_data, 0); - } else - retval = -1; - - if (retval >= 0) - do_mmap(NULL, ELF_PAGESTART(interp_ex->a_text + interp_ex->a_data + ELF_EXEC_PAGESIZE - 1), - interp_ex->a_bss, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); - if (retval < 0) { - return ~0UL; - } - return elf_entry; + unsigned long text_data, offset, elf_entry = ~0UL; + char * addr; + int retval; + + current->mm->end_code = interp_ex->a_text; + text_data = interp_ex->a_text + interp_ex->a_data; + current->mm->end_data = text_data; + current->mm->brk = interp_ex->a_bss + text_data; + + switch (N_MAGIC(*interp_ex)) { + case OMAGIC: + offset = 32; + addr = (char *) 0; + break; + case ZMAGIC: + case QMAGIC: + offset = N_TXTOFF(*interp_ex); + addr = (char *) N_TXTADDR(*interp_ex); + break; + default: + goto out; + } + + do_mmap(NULL, 0, text_data, + PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); + retval = read_exec(interpreter_dentry, offset, addr, text_data, 0); + if (retval < 0) + goto out; + + do_mmap(NULL, ELF_PAGESTART(text_data + ELF_EXEC_PAGESIZE - 1), + interp_ex->a_bss, + PROT_READ|PROT_WRITE|PROT_EXEC, MAP_FIXED|MAP_PRIVATE, 0); + elf_entry = interp_ex->a_entry; + +out: + return elf_entry; } /* @@ -438,7 +473,6 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) unsigned long elf_entry, interp_load_addr = 0; int status; unsigned long start_code, end_code, end_data; - unsigned long elf_stack; char passed_fileno[6]; ibcs2_interpreter = 0; @@ -496,7 +530,6 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) file = current->files->fd[elf_exec_fileno]; - elf_stack = ~0UL; elf_interpreter = NULL; start_code = ~0UL; end_code = 0; @@ -611,34 +644,29 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) bprm->argc++; } } - if (!bprm->p) { - if (elf_interpreter) { - kfree(elf_interpreter); - } - kfree (elf_phdata); - sys_close(elf_exec_fileno); - return -E2BIG; - } + retval = -E2BIG; + if (!bprm->p) + goto out_free_dentry; } /* Flush all traces of the currently running executable */ retval = flush_old_exec(bprm); if (retval) - return retval; + goto out_free_dentry; /* OK, This is the point of no return */ current->mm->end_data = 0; current->mm->end_code = 0; - current->mm->start_mmap = ELF_START_MMAP; current->mm->mmap = NULL; elf_entry = (unsigned long) elf_ex.e_entry; + /* Do this immediately, since STACK_TOP as used in setup_arg_pages + may depend on the personality. */ + SET_PERSONALITY(elf_ex, ibcs2_interpreter); + /* Do this so that we can load the interpreter, if need be. We will change some of these later */ current->mm->rss = 0; -#ifdef ELF_FLAGS_INIT - ELF_FLAGS_INIT; -#endif bprm->p = setup_arg_pages(bprm->p, bprm); current->mm->start_stack = bprm->p; @@ -679,11 +707,6 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) (elf_ppnt->p_offset - ELF_PAGEOFFSET(elf_ppnt->p_vaddr))); -#ifdef LOW_ELF_STACK - if (error < elf_stack) - elf_stack = error-1; -#endif - if (!load_addr_set) { load_addr_set = 1; load_addr = (elf_ppnt->p_vaddr - @@ -696,15 +719,19 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) k = elf_ppnt->p_vaddr; if (k < start_code) start_code = k; k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz; - if (k > elf_bss) elf_bss = k; + if (k > elf_bss) + elf_bss = k; if ((elf_ppnt->p_flags & PF_X) && end_code < k) end_code = k; - if (end_data < k) end_data = k; + if (end_data < k) + end_data = k; k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; - if (k > elf_brk) elf_brk = k; + if (k > elf_brk) + elf_brk = k; } } set_fs(old_fs); + fput(file); /* all done with the file */ elf_entry += load_bias; elf_bss += load_bias; @@ -714,10 +741,10 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) end_data += load_bias; if (elf_interpreter) { - if (interpreter_type & 1) + if (interpreter_type == INTERPRETER_AOUT) elf_entry = load_aout_interp(&interp_ex, interpreter_dentry); - else if (interpreter_type & 2) + else elf_entry = load_elf_interp(&interp_elf_ex, interpreter_dentry, &interp_load_addr); @@ -726,7 +753,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) kfree(elf_interpreter); if (elf_entry == ~0UL) { - printk("Unable to load interpreter\n"); + printk(KERN_ERR "Unable to load interpreter\n"); kfree(elf_phdata); send_sig(SIGSEGV, current, 0); return 0; @@ -735,8 +762,8 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) kfree(elf_phdata); - if (interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno); - current->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX); + if (interpreter_type != INTERPRETER_AOUT) + sys_close(elf_exec_fileno); if (current->exec_domain && current->exec_domain->module) __MOD_DEC_USE_COUNT(current->exec_domain->module); @@ -752,9 +779,6 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) #ifndef VM_STACK_FLAGS current->executable = dget(bprm->dentry); #endif -#ifdef LOW_ELF_STACK - current->start_stack = bprm->p = elf_stack; -#endif current->suid = current->euid = current->fsuid = bprm->e_uid; current->sgid = current->egid = current->fsgid = bprm->e_gid; current->flags &= ~PF_FORKNOEXEC; @@ -766,16 +790,18 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) load_addr, interp_load_addr, (interpreter_type == INTERPRETER_AOUT ? 0 : 1)); + /* N.B. passed_fileno might not be initialized? */ if (interpreter_type == INTERPRETER_AOUT) - current->mm->arg_start += strlen(passed_fileno) + 1; + current->mm->arg_start += strlen(passed_fileno) + 1; current->mm->start_brk = current->mm->brk = elf_brk; current->mm->end_code = end_code; current->mm->start_code = start_code; current->mm->end_data = end_data; current->mm->start_stack = bprm->p; - /* Calling set_brk effectively mmaps the pages that we need for the bss and break - sections */ + /* Calling set_brk effectively mmaps the pages that we need + * for the bss and break sections + */ set_brk(elf_bss, elf_brk); padzero(elf_bss); @@ -795,6 +821,7 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) and some applications "depend" upon this behavior. Since we do not have the power to recompile these, we emulate the SVr4 behavior. Sigh. */ + /* N.B. Shouldn't the size here be PAGE_SIZE?? */ error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE, 0); } @@ -809,11 +836,25 @@ do_load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) ELF_PLAT_INIT(regs); #endif - start_thread(regs, elf_entry, bprm->p); if (current->flags & PF_PTRACED) send_sig(SIGTRAP, current, 0); - return 0; + retval = 0; +out: + return retval; + + /* error cleanup */ +out_free_dentry: + dput(interpreter_dentry); +out_free_interp: + if (elf_interpreter) + kfree(elf_interpreter); +out_free_file: + fput(file); + sys_close(elf_exec_fileno); +out_free_ph: + kfree(elf_phdata); + goto out; } static int @@ -831,75 +872,66 @@ load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) a.out library that is given an ELF header. */ static inline int -do_load_elf_library(int fd){ +do_load_elf_library(int fd) +{ struct file * file; - struct elfhdr elf_ex; - struct elf_phdr *elf_phdata = NULL; struct dentry * dentry; struct inode * inode; - unsigned long len; - int elf_bss; - int retval; - unsigned long bss; - int error; - int i,j, k; + struct elf_phdr *elf_phdata; + unsigned long elf_bss = 0, bss, len, k; + int retval, error, i, j; + struct elfhdr elf_ex; + loff_t offset = 0; - len = 0; - file = current->files->fd[fd]; + error = -EACCES; + file = fget(fd); + if (!file || !file->f_op) + goto out; dentry = file->f_dentry; inode = dentry->d_inode; - elf_bss = 0; - - if (!file || !file->f_op) - return -EACCES; /* seek to the beginning of the file */ - if (file->f_op->llseek) { - if ((error = file->f_op->llseek(file, 0, 0)) != 0) - return -ENOEXEC; - } else - file->f_pos = 0; + error = -ENOEXEC; + /* N.B. save current DS?? */ set_fs(KERNEL_DS); - error = file->f_op->read(file, (char *) &elf_ex, - sizeof(elf_ex), &file->f_pos); + retval = file->f_op->read(file, (char *) &elf_ex, sizeof(elf_ex), &offset); set_fs(USER_DS); - if (error != sizeof(elf_ex)) - return -ENOEXEC; + if (retval != sizeof(elf_ex)) + goto out_putf; if (elf_ex.e_ident[0] != 0x7f || - strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) - return -ENOEXEC; + strncmp(&elf_ex.e_ident[1], "ELF", 3) != 0) + goto out_putf; /* First of all, some simple consistency checks */ if (elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || !elf_check_arch(elf_ex.e_machine) || (!inode->i_op || !inode->i_op->default_file_ops->mmap)) - return -ENOEXEC; + goto out_putf; /* Now read in all of the header information */ - if (sizeof(struct elf_phdr) * elf_ex.e_phnum > ELF_EXEC_PAGESIZE) - return -ENOEXEC; + j = sizeof(struct elf_phdr) * elf_ex.e_phnum; + if (j > ELF_EXEC_PAGESIZE) + goto out_putf; - elf_phdata = (struct elf_phdr *) - kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL); - if (elf_phdata == NULL) - return -ENOMEM; + error = -ENOMEM; + elf_phdata = (struct elf_phdr *) kmalloc(j, GFP_KERNEL); + if (!elf_phdata) + goto out_putf; + /* N.B. check for error return?? */ retval = read_exec(dentry, elf_ex.e_phoff, (char *) elf_phdata, sizeof(struct elf_phdr) * elf_ex.e_phnum, 1); - j = 0; - for(i=0; i<elf_ex.e_phnum; i++) + error = -ENOEXEC; + for (j = 0, i = 0; i<elf_ex.e_phnum; i++) if ((elf_phdata + i)->p_type == PT_LOAD) j++; + if (j != 1) + goto out_free_ph; - if (j != 1) { - kfree(elf_phdata); - return -ENOEXEC; - } - - while(elf_phdata->p_type != PT_LOAD) elf_phdata++; + while (elf_phdata->p_type != PT_LOAD) elf_phdata++; /* Now use mmap to map the library into memory. */ error = do_mmap(file, @@ -910,25 +942,29 @@ do_load_elf_library(int fd){ MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE, (elf_phdata->p_offset - ELF_PAGEOFFSET(elf_phdata->p_vaddr))); + if (error != ELF_PAGESTART(elf_phdata->p_vaddr)) + goto out_free_ph; k = elf_phdata->p_vaddr + elf_phdata->p_filesz; - if (k > elf_bss) elf_bss = k; - - if (error != ELF_PAGESTART(elf_phdata->p_vaddr)) { - kfree(elf_phdata); - return error; - } - + if (k > elf_bss) + elf_bss = k; padzero(elf_bss); - len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr+ ELF_EXEC_PAGESIZE - 1); + len = ELF_PAGESTART(elf_phdata->p_filesz + elf_phdata->p_vaddr + + ELF_EXEC_PAGESIZE - 1); bss = elf_phdata->p_memsz + elf_phdata->p_vaddr; if (bss > len) - do_mmap(NULL, len, bss-len, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_FIXED|MAP_PRIVATE, 0); + do_mmap(NULL, len, bss - len, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + error = 0; + +out_free_ph: kfree(elf_phdata); - return 0; +out_putf: + fput(file); +out: + return error; } static int load_elf_library(int fd) @@ -1187,8 +1223,8 @@ static int elf_core_dump(long signr, struct pt_regs * regs) notes[0].datasz = sizeof(prstatus); notes[0].data = &prstatus; prstatus.pr_info.si_signo = prstatus.pr_cursig = signr; - prstatus.pr_sigpend = current->signal; - prstatus.pr_sighold = current->blocked; + prstatus.pr_sigpend = current->signal.sig[0]; + prstatus.pr_sighold = current->blocked.sig[0]; psinfo.pr_pid = prstatus.pr_pid = current->pid; psinfo.pr_ppid = prstatus.pr_ppid = current->p_pptr->pid; psinfo.pr_pgrp = prstatus.pr_pgrp = current->pgrp; |