/* * linux/fs/binfmt_som.c * * These are the functions used to load SOM format executables as used * by HP-UX. * * Copyright 1999 Matthew Wilcox <willy@bofh.ai> * based on binfmt_elf which is * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). */ #include <linux/module.h> #include <linux/fs.h> #include <linux/stat.h> #include <linux/sched.h> #include <linux/mm.h> #include <linux/mman.h> #include <linux/errno.h> #include <linux/signal.h> #include <linux/binfmts.h> #include <linux/som.h> #include <linux/string.h> #include <linux/file.h> #include <linux/fcntl.h> #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/shm.h> #include <linux/personality.h> #include <linux/init.h> #include <asm/uaccess.h> #include <asm/pgtable.h> #include <linux/elf.h> static int load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs); static int load_som_library(struct file *); /* * If we don't support core dumping, then supply a NULL so we * don't even try. */ #if 0 static int som_core_dump(long signr, struct pt_regs * regs); #else #define som_core_dump NULL #endif #define SOM_PAGESTART(_v) ((_v) & ~(unsigned long)(SOM_PAGESIZE-1)) #define SOM_PAGEOFFSET(_v) ((_v) & (SOM_PAGESIZE-1)) #define SOM_PAGEALIGN(_v) (((_v) + SOM_PAGESIZE - 1) & ~(SOM_PAGESIZE - 1)) static struct linux_binfmt som_format = { .module = THIS_MODULE, .load_binary = load_som_binary, .load_shlib = load_som_library, .core_dump = som_core_dump, .min_coredump = SOM_PAGESIZE }; /* * create_som_tables() parses the env- and arg-strings in new user * memory and creates the pointer tables from them, and puts their * addresses on the "stack", returning the new stack pointer value. */ static void create_som_tables(struct linux_binprm *bprm) { char **argv, **envp; int argc = bprm->argc; int envc = bprm->envc; unsigned long p; unsigned long *sp; /* Word-align the stack pointer */ sp = (unsigned long *)((bprm->p + 3) & ~3); envp = (char **) sp; sp += envc + 1; argv = (char **) sp; sp += argc + 1; __put_user((unsigned long) envp,++sp); __put_user((unsigned long) argv,++sp); __put_user(argc, ++sp); bprm->p = (unsigned long) sp; p = current->mm->arg_start; while (argc-- > 0) { __put_user((char *)p,argv++); p += strlen_user((char *)p); } __put_user(NULL, argv); current->mm->arg_end = current->mm->env_start = p; while (envc-- > 0) { __put_user((char *)p,envp++); p += strlen_user((char *)p); } __put_user(NULL, envp); current->mm->env_end = p; } static int check_som_header(struct som_hdr *som_ex) { int *buf = (int *)som_ex; int i, ck; if (som_ex->system_id != SOM_SID_PARISC_1_0 && som_ex->system_id != SOM_SID_PARISC_1_1 && som_ex->system_id != SOM_SID_PARISC_2_0) return -ENOEXEC; if (som_ex->a_magic != SOM_EXEC_NONSHARE && som_ex->a_magic != SOM_EXEC_SHARE && som_ex->a_magic != SOM_EXEC_DEMAND) return -ENOEXEC; if (som_ex->version_id != SOM_ID_OLD && som_ex->version_id != SOM_ID_NEW) return -ENOEXEC; ck = 0; for (i=0; i<32; i++) ck ^= buf[i]; if (ck != 0) return -ENOEXEC; return 0; } static int map_som_binary(struct file *file, const struct som_exec_auxhdr *hpuxhdr) { unsigned long code_start, code_size, data_start, data_size; unsigned long bss_start, som_brk; int retval; int prot = PROT_READ | PROT_EXEC; int flags = MAP_FIXED|MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE; mm_segment_t old_fs = get_fs(); set_fs(get_ds()); code_start = SOM_PAGESTART(hpuxhdr->exec_tmem); code_size = SOM_PAGEALIGN(hpuxhdr->exec_tsize); current->mm->start_code = code_start; current->mm->end_code = code_start + code_size; down_write(¤t->mm->mmap_sem); retval = do_mmap(file, code_start, code_size, prot, flags, SOM_PAGESTART(hpuxhdr->exec_tfile)); up_write(¤t->mm->mmap_sem); if (retval < 0 && retval > -1024) goto out; data_start = SOM_PAGESTART(hpuxhdr->exec_dmem); data_size = SOM_PAGEALIGN(hpuxhdr->exec_dsize); current->mm->start_data = data_start; current->mm->end_data = bss_start = data_start + data_size; down_write(¤t->mm->mmap_sem); retval = do_mmap(file, data_start, data_size, prot | PROT_WRITE, flags, SOM_PAGESTART(hpuxhdr->exec_dfile)); up_write(¤t->mm->mmap_sem); if (retval < 0 && retval > -1024) goto out; som_brk = bss_start + SOM_PAGEALIGN(hpuxhdr->exec_bsize); current->mm->start_brk = current->mm->brk = som_brk; down_write(¤t->mm->mmap_sem); retval = do_mmap(NULL, bss_start, som_brk - bss_start, prot | PROT_WRITE, MAP_FIXED | MAP_PRIVATE, 0); up_write(¤t->mm->mmap_sem); if (retval > 0 || retval < -1024) retval = 0; out: set_fs(old_fs); return retval; } /* * These are the functions used to load SOM executables and shared * libraries. There is no binary dependent code anywhere else. */ static int load_som_binary(struct linux_binprm * bprm, struct pt_regs * regs) { int som_exec_fileno; int retval; unsigned int size; unsigned long som_entry; struct som_hdr *som_ex; struct som_exec_auxhdr *hpuxhdr; /* Get the exec-header */ som_ex = (struct som_hdr *) bprm->buf; retval = check_som_header(som_ex); if (retval != 0) goto out; /* Now read in the auxiliary header information */ retval = -ENOMEM; size = som_ex->aux_header_size; if (size > SOM_PAGESIZE) goto out; hpuxhdr = (struct som_exec_auxhdr *) kmalloc(size, GFP_KERNEL); if (!hpuxhdr) goto out; retval = kernel_read(bprm->file, som_ex->aux_header_location, (char *) hpuxhdr, size); if (retval < 0) goto out_free; #error "Fix security hole before enabling me" retval = get_unused_fd(); if (retval < 0) goto out_free; get_file(bprm->file); fd_install(som_exec_fileno = retval, bprm->file); /* Flush all traces of the currently running executable */ retval = flush_old_exec(bprm); if (retval) goto out_free; /* OK, This is the point of no return */ current->flags &= ~PF_FORKNOEXEC; current->personality = PER_HPUX; /* Set the task size for HP-UX processes such that * the gateway page is outside the address space. * This can be fixed later, but for now, this is much * easier. */ current->thread.task_size = 0xc0000000; /* Set map base to allow enough room for hp-ux heap growth */ current->thread.map_base = 0x80000000; retval = map_som_binary(bprm->file, hpuxhdr); if (retval < 0) goto out_free; som_entry = hpuxhdr->exec_entry; kfree(hpuxhdr); set_binfmt(&som_format); compute_creds(bprm); setup_arg_pages(bprm, STACK_TOP, EXSTACK_DEFAULT); create_som_tables(bprm); current->mm->start_stack = bprm->p; #if 0 printk("(start_brk) %08lx\n" , (unsigned long) current->mm->start_brk); printk("(end_code) %08lx\n" , (unsigned long) current->mm->end_code); printk("(start_code) %08lx\n" , (unsigned long) current->mm->start_code); printk("(end_data) %08lx\n" , (unsigned long) current->mm->end_data); printk("(start_stack) %08lx\n" , (unsigned long) current->mm->start_stack); printk("(brk) %08lx\n" , (unsigned long) current->mm->brk); #endif map_hpux_gateway_page(current,current->mm); start_thread_som(regs, som_entry, bprm->p); if (current->ptrace & PT_PTRACED) send_sig(SIGTRAP, current, 0); return 0; /* error cleanup */ out_free: kfree(hpuxhdr); out: return retval; } static int load_som_library(struct file *f) { /* No lib support in SOM yet. gizza chance.. */ return -ENOEXEC; } /* Install the SOM loader. * N.B. We *rely* on the table being the right size with the * right number of free slots... */ static int __init init_som_binfmt(void) { return register_binfmt(&som_format); } static void __exit exit_som_binfmt(void) { /* Remove the SOM loader. */ unregister_binfmt(&som_format); } core_initcall(init_som_binfmt); module_exit(exit_som_binfmt);