diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
commit | 1513ff9b7899ab588401c89db0e99903dbf5f886 (patch) | |
tree | f69cc81a940a502ea23d664c3ffb2d215a479667 /ibcs |
Import of Linus's Linux 1.1.68
Diffstat (limited to 'ibcs')
-rw-r--r-- | ibcs/Makefile | 37 | ||||
-rw-r--r-- | ibcs/binfmt_coff.c | 784 | ||||
-rw-r--r-- | ibcs/binfmt_elf.c | 656 | ||||
-rw-r--r-- | ibcs/emulate.c | 10 |
4 files changed, 1487 insertions, 0 deletions
diff --git a/ibcs/Makefile b/ibcs/Makefile new file mode 100644 index 000000000..7b81ff2bc --- /dev/null +++ b/ibcs/Makefile @@ -0,0 +1,37 @@ +# +# Makefile for the iBCS emulator files +# +# Note! Dependencies are done automagically by 'make dep', which also +# removes any old dependencies. DON'T put your own dependencies here +# unless it's something special (ie not a .c file). +# +# Note 2! The CFLAGS definitions are now in the main makefile... + +.S.s: + $(CPP) -traditional $< -o $*.s +.c.s: + $(CC) $(CFLAGS) -S $< +.s.o: + $(AS) -c -o $*.o $< +.c.o: + $(CC) $(CFLAGS) -c $< + +SUBDIRS = + +OBJS = emulate.o + +ibcs.o: $(OBJS) + $(LD) -r -o ibcs.o $(OBJS) + sync + +dep: + $(CPP) -M *.c > .depend + +dummy: + +# +# include a dependency file if one exists +# +ifeq (.depend,$(wildcard .depend)) +include .depend +endif diff --git a/ibcs/binfmt_coff.c b/ibcs/binfmt_coff.c new file mode 100644 index 000000000..1a7c760cd --- /dev/null +++ b/ibcs/binfmt_coff.c @@ -0,0 +1,784 @@ +/* + * These are the functions used to load COFF IBSC style executables. + * Information on COFF format may be obtained in either the Intel Binary + * Compatibility Specification 2 or O'Rilley's book on COFF. The shared + * libraries are defined only the in the Intel book. + * + * This file is based upon code written by Eric Youngdale for the ELF object + * file format. + * + * Author: Al Longyear (longyear@sii.com) + * + * Latest Revision: + * 3 February 1994 + * Al Longyear (longyear@sii.com) + * Cleared first page of bss section using put_fs_byte. + */ + +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/a.out.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/binfmts.h> +#include <asm/segment.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/coff.h> +#include <linux/malloc.h> + +asmlinkage int sys_exit (int exit_code); +asmlinkage int sys_close (unsigned fd); +asmlinkage int sys_open (const char *, int, int); +asmlinkage int sys_uselib(const char * library); + +static int preload_library (struct linux_binprm *exe_bprm, + COFF_SCNHDR * sect, + struct file *fp); + +static int load_object (struct linux_binprm *bprm, + struct pt_regs *regs, + int lib_ok); + +/* + * Small procedure to test for the proper file alignment. + */ + +static inline int +is_properly_aligned (COFF_SCNHDR *sect) +{ + long scnptr = COFF_LONG (sect->s_scnptr); + long vaddr = COFF_LONG (sect->s_vaddr); +/* + * Print the section information if needed + */ + +#ifdef COFF_DEBUG + printk ("%s, scnptr = %d, vaddr = %d\n", + sect->s_name, + scnptr, vaddr); +#endif + +/* + * Return the error code if the section is not properly aligned. + */ + +#ifdef COFF_DEBUG + if (((vaddr - scnptr) & ~PAGE_MASK) != 0) + printk ("bad alignment in %s\n", sect->s_name); +#endif + return ((((vaddr - scnptr) & ~PAGE_MASK) != 0) ? -ENOEXEC : 0); +} + +/* + * Clear the bytes in the last page of data. + */ + +static +int clear_memory (unsigned long addr, unsigned long size) +{ + int status; + + size = (PAGE_SIZE - (addr & ~PAGE_MASK)) & ~PAGE_MASK; + if (size == 0) + status = 0; + else { + +#ifdef COFF_DEBUG + printk ("un-initialized storage in last page %d\n", size); +#endif + + status = verify_area (VERIFY_WRITE, + (void *) addr, size); +#ifdef COFF_DEBUG + printk ("result from verify_area = %d\n", status); +#endif + + if (status >= 0) + while (size-- != 0) + put_fs_byte (0, addr++); + } + return status; +} + +/* + * Helper function to process the load operation. + */ + +static int +load_object (struct linux_binprm * bprm, struct pt_regs *regs, int lib_ok) +{ + COFF_FILHDR *coff_hdr = (COFF_FILHDR *) bprm->buf; /* COFF Header */ + COFF_SCNHDR *sect_bufr; /* Pointer to section table */ + COFF_SCNHDR *text_sect; /* Pointer to the text section */ + COFF_SCNHDR *data_sect; /* Pointer to the data section */ + COFF_SCNHDR *bss_sect; /* Pointer to the bss section */ + int text_count; /* Number of text sections */ + int data_count; /* Number of data sections */ + int bss_count; /* Number of bss sections */ + int lib_count; /* Number of lib sections */ + unsigned int start_addr = 0;/* Starting location for program */ + int status = 0; /* Result status register */ + int fd = -1; /* Open file descriptor */ + struct file *fp = NULL; /* Pointer to the file at "fd" */ + short int sections = 0; /* Number of sections in the file */ + short int aout_size = 0; /* Size of the a.out header area */ + short int flags; /* Flag bits from the COFF header */ + +#ifdef COFF_DEBUG + printk ("binfmt_coff entry: %s\n", bprm->filename); +#endif + +/* + * Validate the magic value for the object file. + */ + do { + if (COFF_I386BADMAG (*coff_hdr)) { +#ifdef COFF_DEBUG + printk ("bad filehdr magic\n"); +#endif + status = -ENOEXEC; + break; + } +/* + * The object file should have 32 BIT little endian format. Do not allow + * it to have the 16 bit object file flag set as Linux is not able to run + * on the 80286/80186/8086. + */ + flags = COFF_SHORT (coff_hdr->f_flags); + if ((flags & (COFF_F_AR32WR | COFF_F_AR16WR)) != COFF_F_AR32WR) { +#ifdef COFF_DEBUG + printk ("invalid f_flags bits\n"); +#endif + status = -ENOEXEC; + break; + } +/* + * Extract the header information which we need. + */ + sections = COFF_SHORT (coff_hdr->f_nscns); /* Number of sections */ + aout_size = COFF_SHORT (coff_hdr->f_opthdr); /* Size of opt. headr */ +/* + * If the file is not executable then reject the execution. This means + * that there must not be external references. + */ + if ((flags & COFF_F_EXEC) == 0) { +#ifdef COFF_DEBUG + printk ("not executable bit\n"); +#endif + status = -ENOEXEC; + break; + } +/* + * There must be at least one section. + */ + if (sections == 0) { +#ifdef COFF_DEBUG + printk ("no sections\n"); +#endif + status = -ENOEXEC; + break; + } +/* + * Do some additional consistency checks. + * The system requires mapping for this loader. If you try + * to use a file system with no mapping, the format is not valid. + */ + if (!bprm->inode->i_op || + !bprm->inode->i_op->default_file_ops->mmap) { +#ifdef COFF_DEBUG + printk ("no mmap in fs\n"); +#endif + status = -ENOEXEC; + } + } + while (0); +/* + * Allocate a buffer to hold the entire coff section list. + */ + if (status >= 0) { + int nbytes = sections * COFF_SCNHSZ; + + sect_bufr = (COFF_SCNHDR *) kmalloc (nbytes, GFP_KERNEL); + if (0 == sect_bufr) { +#ifdef COFF_DEBUG + printk ("kmalloc failed\n"); +#endif + status = -ENOEXEC; + } +/* + * Read the section list from the disk file. + */ + else { + int old_fs = get_fs (); + set_fs (get_ds ()); /* Make it point to the proper location */ + status = read_exec (bprm->inode, /* INODE for file */ + aout_size + COFF_FILHSZ, /* Offset in the file */ + (char *) sect_bufr, /* Buffer for read */ + nbytes); /* Byte count reqd. */ + set_fs (old_fs); /* Restore the selector */ +#ifdef COFF_DEBUG + if (status < 0) + printk ("read aout hdr, status = %d\n", status); +#endif + } + } + else + sect_bufr = NULL; /* Errors do not have a section buffer */ +/* + * Count the number of sections for the required types and store the location + * of the last section for the three primary types. + */ + text_count = 0; + data_count = 0; + bss_count = 0; + lib_count = 0; + + text_sect = NULL; + data_sect = NULL; + bss_sect = NULL; +/* + * Loop through the sections and find the various types + */ + if (status >= 0) { + int nIndex; + COFF_SCNHDR *sect_ptr = sect_bufr; + + for (nIndex = 0; nIndex < sections; ++nIndex) { + long int sect_flags = COFF_LONG (sect_ptr->s_flags); + + switch (sect_flags) { + case COFF_STYP_TEXT: + text_sect = sect_ptr; + ++text_count; + status = is_properly_aligned (sect_ptr); + break; + + case COFF_STYP_DATA: + data_sect = sect_ptr; + ++data_count; + status = is_properly_aligned (sect_ptr); + break; + + case COFF_STYP_BSS: + bss_sect = sect_ptr; + ++bss_count; + break; + + case COFF_STYP_LIB: +#ifdef COFF_DEBUG + printk (".lib section found\n"); +#endif + ++lib_count; + break; + + default: + break; + } + sect_ptr = (COFF_SCNHDR *) & ((char *) sect_ptr)[COFF_SCNHSZ]; + } +/* + * Ensure that there are the required sections. There must be one text + * sections and one each of the data and bss sections for an executable. + * A library may or may not have a data / bss section. + */ + if (text_count != 1) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("no text sections\n"); +#endif + } + else { + if (lib_ok) { + if (data_count != 1 || bss_count != 1) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("no .data nor .bss sections\n"); +#endif + } + } + } + } +/* + * If there is no additional header then assume the file starts at + * the first byte of the text section. This may not be the proper place, + * so the best solution is to include the optional header. A shared library + * __MUST__ have an optional header to indicate that it is a shared library. + */ + if (status >= 0) { + if (aout_size == 0) { + if (!lib_ok) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("no header in library\n"); +#endif + } + start_addr = COFF_LONG (text_sect->s_vaddr); + } +/* + * There is some header. Ensure that it is sufficient. + */ + else { + if (aout_size < COFF_AOUTSZ) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("header too small\n"); +#endif + } + else { + COFF_AOUTHDR *aout_hdr = /* Pointer to a.out header */ + (COFF_AOUTHDR *) & ((char *) coff_hdr)[COFF_FILHSZ]; + short int aout_magic = COFF_SHORT (aout_hdr->magic); /* id */ +/* + * Validate the magic number in the a.out header. If it is valid then + * update the starting symbol location. Do not accept these file formats + * when loading a shared library. + */ + switch (aout_magic) { + case COFF_OMAGIC: + case COFF_ZMAGIC: + case COFF_STMAGIC: + if (!lib_ok) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("wrong a.out header magic\n"); +#endif + } + start_addr = (unsigned int) COFF_LONG (aout_hdr->entry); + break; +/* + * Magic value for a shared library. This is valid only when loading a + * shared library. (There is no need for a start_addr. It won't be used.) + */ + case COFF_SHMAGIC: + if (lib_ok) { +#ifdef COFF_DEBUG + printk ("wrong a.out header magic\n"); +#endif + status = -ENOEXEC; + } + break; + + default: +#ifdef COFF_DEBUG + printk ("wrong a.out header magic\n"); +#endif + status = -ENOEXEC; + break; + } + } + } + } +/* + * Fetch a file pointer to the executable. + */ + if (status >= 0) { + fd = open_inode (bprm->inode, O_RDONLY); + if (fd < 0) { +#ifdef COFF_DEBUG + printk ("can not open inode, result = %d\n", fd); +#endif + status = fd; + } + else + fp = current->files->fd[fd]; + } + else + fd = -1; /* Invalidate the open file descriptor */ +/* + * Generate the proper values for the text fields + * + * THIS IS THE POINT OF NO RETURN. THE NEW PROCESS WILL TRAP OUT SHOULD + * SOMETHING FAIL IN THE LOAD SEQUENCE FROM THIS POINT ONWARD. + */ + if (status >= 0) { + long text_scnptr = COFF_LONG (text_sect->s_scnptr); + long text_size = COFF_LONG (text_sect->s_size); + long text_vaddr = COFF_LONG (text_sect->s_vaddr); + + long data_scnptr; + long data_size; + long data_vaddr; + + long bss_size; + long bss_vaddr; +/* + * Generate the proper values for the data fields + */ + if (data_sect != NULL) { + data_scnptr = COFF_LONG (data_sect->s_scnptr); + data_size = COFF_LONG (data_sect->s_size); + data_vaddr = COFF_LONG (data_sect->s_vaddr); + } + else { + data_scnptr = 0; + data_size = 0; + data_vaddr = 0; + } +/* + * Generate the proper values for the bss fields + */ + if (bss_sect != NULL) { + bss_size = COFF_LONG (bss_sect->s_size); + bss_vaddr = COFF_LONG (bss_sect->s_vaddr); + } + else { + bss_size = 0; + bss_vaddr = 0; + } +/* + * Flush the executable from memory. At this point the executable is + * committed to being defined or a segmentation violation will occur. + */ + if (lib_ok) { +#ifdef COFF_DEBUG + printk ("flushing executable\n"); +#endif + flush_old_exec (bprm); +/* + * Define the initial locations for the various items in the new process + */ + current->mm->mmap = NULL; + current->mm->rss = 0; +/* + * Construct the parameter and environment string table entries. + */ + bprm->p += change_ldt (0, bprm->page); + bprm->p -= MAX_ARG_PAGES*PAGE_SIZE; + bprm->p = (unsigned long) create_tables ((char *) bprm->p, + bprm->argc, + bprm->envc, + 1); +/* + * Do the end processing once the stack has been constructed + */ + current->mm->start_code = text_vaddr & PAGE_MASK; + current->mm->end_code = text_vaddr + text_size; + current->mm->end_data = data_vaddr + data_size; + current->mm->start_brk = + current->mm->brk = bss_vaddr + bss_size; + current->suid = + current->euid = bprm->e_uid; + current->sgid = + current->egid = bprm->e_gid; + current->executable = bprm->inode; /* Store inode for file */ + ++bprm->inode->i_count; /* Count the open inode */ + regs->eip = start_addr; /* Current EIP register */ + regs->esp = + current->mm->start_stack = bprm->p; + } +/* + * Map the text pages + */ + +#ifdef COFF_DEBUG + printk (".text: vaddr = %d, size = %d, scnptr = %d\n", + text_vaddr, + text_size, + text_scnptr); +#endif + status = do_mmap (fp, + text_vaddr & PAGE_MASK, + text_size + (text_vaddr & ~PAGE_MASK), + PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_SHARED, + text_scnptr & PAGE_MASK); + + status = (status == (text_vaddr & PAGE_MASK)) ? 0 : -ENOEXEC; +/* + * Map the data pages + */ + if (status >= 0 && data_size != 0) { +#ifdef COFF_DEBUG + printk (".data: vaddr = %d, size = %d, scnptr = %d\n", + data_vaddr, + data_size, + data_scnptr); +#endif + status = do_mmap (fp, + data_vaddr & PAGE_MASK, + data_size + (data_vaddr & ~PAGE_MASK), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, + data_scnptr & PAGE_MASK); + + status = (status == (data_vaddr & PAGE_MASK)) ? 0 : -ENOEXEC; + } +/* + * Construct the bss data for the process. The bss ranges from the + * end of the data (which may not be on a page boundary) to the end + * of the bss section. Allocate any necessary pages for the data. + */ + if (status >= 0 && bss_size != 0) { +#ifdef COFF_DEBUG + printk (".bss: vaddr = %d, size = %d\n", + bss_vaddr, + bss_size); +#endif + zeromap_page_range (PAGE_ALIGN (bss_vaddr), + PAGE_ALIGN (bss_size), + PAGE_COPY); + + status = clear_memory (bss_vaddr, bss_size); + } +/* + * Load any shared library for the executable. + */ + if (status >= 0 && lib_ok && lib_count != 0) { + int nIndex; + COFF_SCNHDR *sect_ptr = sect_bufr; +/* + * Find the library sections. (There should be at least one. It was counted + * earlier.) This will eventually recurse to our code and load the shared + * library with our own procedures. + */ + for (nIndex = 0; nIndex < sections; ++nIndex) { + long int sect_flags = COFF_LONG (sect_ptr->s_flags); + if (sect_flags == COFF_STYP_LIB) { + status = preload_library (bprm, sect_ptr, fp); + if (status != 0) + break; + } + sect_ptr = (COFF_SCNHDR *) &((char *) sect_ptr) [COFF_SCNHSZ]; + } + } +/* + * Generate any needed trap for this process. If an error occurred then + * generate a segmentation violation. If the process is being debugged + * then generate the load trap. (Note: If this is a library load then + * do not generate the trap here. Pass the error to the caller who + * will do it for the process in the outer lay of this procedure call.) + */ + if (lib_ok) { + if (status < 0) + send_sig (SIGSEGV, current, 0); /* Generate the error trap */ + else { + if (current->flags & PF_PTRACED) + send_sig (SIGTRAP, current, 0); + } + status = 0; /* We are committed. It can't fail */ + } + } +/* + * Do any cleanup processing + */ + if (fd >= 0) + sys_close (fd); /* Close unused code file */ + + if (sect_bufr != NULL) + kfree (sect_bufr); /* Release section list buffer */ +/* + * Return the completion status. + */ +#ifdef COFF_DEBUG + printk ("binfmt_coff: result = %d\n", status); +#endif + return (status); +} + +/* + * This procedure will load the library listed in the file name given + * as the parameter. The result will be non-zero should something fail + * to load. + */ + +static int +preload_this_library (struct linux_binprm *exe_bprm, char *lib_name) +{ + int status; + int old_fs = get_fs(); +/* + * If debugging then print "we have arrived" + */ +#ifdef COFF_DEBUG + printk ("%s loading shared library %s\n", + exe_bprm->filename, + lib_name); +#endif +/* + * Change the FS register to the proper kernel address space and attempt + * to load the library. The library name is allocated from the kernel + * pool. + */ + set_fs (get_ds ()); + status = sys_uselib (lib_name); + set_fs (old_fs); +/* + * Return the success/failure to the caller. + */ + return (status); +} + +/* + * This procedure is called to load a library section. The various + * libraries are loaded from the list given in the section data. + */ + +static int +preload_library (struct linux_binprm *exe_bprm, + COFF_SCNHDR * sect, struct file *fp) +{ + int status = 0; /* Completion status */ + long nbytes; /* Count of bytes in the header area */ +/* + * Fetch the size of the section. There must be enough room for at least + * one entry. + */ + nbytes = COFF_LONG (sect->s_size); + if (nbytes < COFF_SLIBSZ) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("library section too small\n"); +#endif + } +/* + * Allocate a buffer to hold the section data + */ + else { + COFF_SLIBHD *phdr; + char *buffer = (char *) kmalloc (nbytes, GFP_KERNEL); + + if (0 == buffer) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("kmalloc failed\n"); +#endif + } + else { + int old_fs = get_fs (); +/* + * Read the section data from the disk file. + */ + set_fs (get_ds ()); /* Make it point to the proper location */ + status = read_exec (exe_bprm->inode, /* INODE for file */ + COFF_LONG (sect->s_scnptr), /* Disk location */ + buffer, /* Buffer for read */ + nbytes); /* Byte count reqd. */ + set_fs (old_fs); /* Restore the selector */ +/* + * Check the result. The value returned is the byte count actually read. + */ + if (status >= 0 && status != nbytes) { +#ifdef COFF_DEBUG + printk ("read of lib section was short\n"); +#endif + status = -ENOEXEC; + } + } +/* + * At this point, go through the list of libraries in the data area. + */ + phdr = (COFF_SLIBHD *) buffer; + while (status >= 0 && nbytes > COFF_SLIBSZ) { + int entry_size = COFF_LONG (phdr->sl_entsz) * sizeof (long); + int header_size = COFF_LONG (phdr->sl_pathndx) * sizeof (long); +/* + * Validate the sizes of the various items. I don't trust the linker!! + */ + if ((unsigned) header_size >= (unsigned) nbytes || + entry_size <= 0 || + (unsigned) entry_size <= (unsigned) header_size) { + status = -ENOEXEC; +#ifdef COFF_DEBUG + printk ("header count is invalid\n"); +#endif + } +/* + * Load the library. Stop the load process on the first error. + */ + else { + status = preload_this_library (exe_bprm, + &((char *) phdr)[header_size]); +#ifdef COFF_DEBUG + printk ("preload_this_library result = %d\n", status); +#endif + } +/* + * Point to the next library in the section data. + */ + nbytes -= entry_size; + phdr = (COFF_SLIBHD *) &((char *) phdr)[entry_size]; + } +/* + * Release the space for the library list. + */ + if (buffer != NULL) + kfree (buffer); + } +/* + * Return the resulting status to the caller. + */ + return (status); +} + +/* + * This procedure is called by the main load sequence. It will load + * the executable and prepare it for execution. It provides the additional + * parameters used by the recursive coff loader and tells the loader that + * this is the main executable. How simple it is . . . . + */ + +int +load_coff_binary (struct linux_binprm *bprm, struct pt_regs *regs) +{ + return (load_object (bprm, regs, 1)); +} + +/* + * Load the image for any shared library. + * + * This is called when we need to load a library based upon a file name. + */ + +int +load_coff_library (int fd) +{ + struct linux_binprm *bprm; /* Parameters for the load operation */ + int status; /* Status of the request */ +/* + * Read the first portion of the file. + */ + bprm = (struct linux_binprm *) kmalloc (sizeof (struct linux_binprm), + GFP_KERNEL); + if (0 == bprm) { +#ifdef COFF_DEBUG + printk ("kmalloc failed\n"); +#endif + status = -ENOEXEC; + } + else { + struct file *file; /* Pointer to the file table */ + struct pt_regs regs; /* Register work area */ + int old_fs = get_fs (); /* Previous FS register value */ + + memset (bprm, '\0', sizeof (struct linux_binprm)); + + file = current->files->fd[fd]; + bprm->inode = file->f_inode; /* The only item _really_ needed */ + bprm->filename = ""; /* Make it a legal string */ +/* + * Read the section list from the disk file. + */ + set_fs (get_ds ()); /* Make it point to the proper location */ + status = read_exec (bprm->inode, /* INODE for file */ + 0L, /* Offset in the file */ + bprm->buf, /* Buffer for read */ + sizeof (bprm->buf)); /* Size of the buffer */ + set_fs (old_fs); /* Restore the selector */ +/* + * Try to load the library. + */ + status = load_object (bprm, ®s, 0); +/* + * Release the work buffer and return the result. + */ + kfree (bprm); /* Release the buffer area */ + } +/* + * Return the result of the load operation + */ + return (status); +} diff --git a/ibcs/binfmt_elf.c b/ibcs/binfmt_elf.c new file mode 100644 index 000000000..1b33470cc --- /dev/null +++ b/ibcs/binfmt_elf.c @@ -0,0 +1,656 @@ +/* + * linux/fs/binfmt_elf.c + * + * These are the functions used to load ELF format executables as used + * on SVr4 machines. Information on the format may be found in the book + * "UNIX SYSTEM V RELEASE 4 Programmers Guide: Ansi C and Programming Support + * Tools". + * + * Copyright 1993, 1994: Eric Youngdale (ericy@cais.com). + */ +#include <linux/fs.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/mman.h> +#include <linux/a.out.h> +#include <linux/errno.h> +#include <linux/signal.h> +#include <linux/binfmts.h> +#include <linux/string.h> +#include <linux/fcntl.h> +#include <linux/ptrace.h> +#include <linux/malloc.h> +#include <linux/shm.h> +#include <linux/personality.h> + +#include <asm/segment.h> + +asmlinkage int sys_exit(int exit_code); +asmlinkage int sys_close(unsigned fd); +asmlinkage int sys_open(const char *, int, int); +asmlinkage int sys_brk(unsigned long); + +#define DLINFO_ITEMS 8 + +#include <linux/elf.h> + +/* We need to explicitly zero any fractional pages + after the data section (i.e. bss). This would + contain the junk from the file that should not + be in memory */ + +static void padzero(int elf_bss){ + unsigned int fpnt, nbyte; + + if(elf_bss & 0xfff) { + + nbyte = (PAGE_SIZE - (elf_bss & 0xfff)) & 0xfff; + if(nbyte){ + verify_area(VERIFY_WRITE, (void *) elf_bss, nbyte); + + fpnt = elf_bss; + while(fpnt & 0xfff) put_fs_byte(0, fpnt++); + }; + }; +} + +unsigned long * create_elf_tables(char * p,int argc,int envc,struct elfhdr * exec, unsigned int load_addr, int ibcs) +{ + unsigned long *argv,*envp, *dlinfo; + unsigned long * sp; + struct vm_area_struct *mpnt; + + mpnt = (struct vm_area_struct *)kmalloc(sizeof(*mpnt), GFP_KERNEL); + if (mpnt) { + mpnt->vm_task = current; + mpnt->vm_start = PAGE_MASK & (unsigned long) p; + mpnt->vm_end = TASK_SIZE; + mpnt->vm_page_prot = PAGE_PRIVATE|PAGE_DIRTY; + mpnt->vm_flags = VM_STACK_FLAGS; + mpnt->vm_share = NULL; + mpnt->vm_ops = NULL; + mpnt->vm_inode = NULL; + mpnt->vm_offset = 0; + mpnt->vm_pte = 0; + insert_vm_struct(current, mpnt); + } + sp = (unsigned long *) (0xfffffffc & (unsigned long) p); + if(exec) sp -= DLINFO_ITEMS*2; + dlinfo = sp; + sp -= envc+1; + envp = sp; + sp -= argc+1; + argv = sp; + if (!ibcs) { + put_fs_long((unsigned long)envp,--sp); + put_fs_long((unsigned long)argv,--sp); + } + + /* The constant numbers (0-9) that we are writing here are + described in the header file sys/auxv.h on at least + some versions of SVr4 */ + if(exec) { /* Put this here for an ELF program interpreter */ + struct elf_phdr * eppnt; + eppnt = (struct elf_phdr *) exec->e_phoff; + put_fs_long(3,dlinfo++); put_fs_long(load_addr + exec->e_phoff,dlinfo++); + put_fs_long(4,dlinfo++); put_fs_long(sizeof(struct elf_phdr),dlinfo++); + put_fs_long(5,dlinfo++); put_fs_long(exec->e_phnum,dlinfo++); + put_fs_long(9,dlinfo++); put_fs_long((unsigned long) exec->e_entry,dlinfo++); + put_fs_long(7,dlinfo++); put_fs_long(SHM_RANGE_START,dlinfo++); + put_fs_long(8,dlinfo++); put_fs_long(0,dlinfo++); + put_fs_long(6,dlinfo++); put_fs_long(PAGE_SIZE,dlinfo++); + put_fs_long(0,dlinfo++); put_fs_long(0,dlinfo++); + }; + + put_fs_long((unsigned long)argc,--sp); + current->mm->arg_start = (unsigned long) p; + while (argc-->0) { + put_fs_long((unsigned long) p,argv++); + while (get_fs_byte(p++)) /* nothing */ ; + } + put_fs_long(0,argv); + current->mm->arg_end = current->mm->env_start = (unsigned long) p; + while (envc-->0) { + put_fs_long((unsigned long) p,envp++); + while (get_fs_byte(p++)) /* nothing */ ; + } + put_fs_long(0,envp); + current->mm->env_end = (unsigned long) p; + return sp; +} + + +/* This is much more generalized than the library routine read function, + so we keep this separate. Technically the library read function + is only provided so that we can read a.out libraries that have + an ELF header */ + +static unsigned int load_elf_interp(struct elfhdr * interp_elf_ex, + struct inode * interpreter_inode) +{ + struct file * file; + struct elf_phdr *elf_phdata = NULL; + struct elf_phdr *eppnt; + unsigned int len; + unsigned int load_addr; + int elf_exec_fileno; + int elf_bss; + int old_fs, retval; + unsigned int last_bss; + int error; + int i, k; + + elf_bss = 0; + last_bss = 0; + error = load_addr = 0; + + /* First of all, some simple consistency checks */ + if((interp_elf_ex->e_type != ET_EXEC && + interp_elf_ex->e_type != ET_DYN) || + (interp_elf_ex->e_machine != EM_386 && interp_elf_ex->e_machine != EM_486) || + (!interpreter_inode->i_op || + !interpreter_inode->i_op->default_file_ops->mmap)){ + return 0xffffffff; + }; + + /* Now read in all of the header information */ + + if(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum > PAGE_SIZE) + return 0xffffffff; + + elf_phdata = (struct elf_phdr *) + kmalloc(sizeof(struct elf_phdr) * interp_elf_ex->e_phnum, GFP_KERNEL); + if(!elf_phdata) return 0xffffffff; + + old_fs = get_fs(); + set_fs(get_ds()); + retval = read_exec(interpreter_inode, interp_elf_ex->e_phoff, (char *) elf_phdata, + sizeof(struct elf_phdr) * interp_elf_ex->e_phnum); + set_fs(old_fs); + + elf_exec_fileno = open_inode(interpreter_inode, O_RDONLY); + if (elf_exec_fileno < 0) return 0xffffffff; + file = current->files->fd[elf_exec_fileno]; + + eppnt = elf_phdata; + for(i=0; i<interp_elf_ex->e_phnum; i++, eppnt++) + if(eppnt->p_type == PT_LOAD) { + error = do_mmap(file, + eppnt->p_vaddr & 0xfffff000, + eppnt->p_filesz + (eppnt->p_vaddr & 0xfff), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | (interp_elf_ex->e_type == ET_EXEC ? MAP_FIXED : 0), + eppnt->p_offset & 0xfffff000); + + if(!load_addr && interp_elf_ex->e_type == ET_DYN) + load_addr = error; + k = load_addr + eppnt->p_vaddr + eppnt->p_filesz; + if(k > elf_bss) elf_bss = k; + if(error < 0 && error > -1024) break; /* Real error */ + k = load_addr + eppnt->p_memsz + eppnt->p_vaddr; + if(k > last_bss) last_bss = k; + } + + /* Now use mmap to map the library into memory. */ + + + sys_close(elf_exec_fileno); + if(error < 0 && error > -1024) { + kfree(elf_phdata); + return 0xffffffff; + } + + padzero(elf_bss); + len = (elf_bss + 0xfff) & 0xfffff000; /* What we have mapped so far */ + + /* Map the last of the bss segment */ + if (last_bss > len) + do_mmap(NULL, len, last_bss-len, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + kfree(elf_phdata); + + return ((unsigned int) interp_elf_ex->e_entry) + load_addr; +} + +static unsigned int load_aout_interp(struct exec * interp_ex, + struct inode * interpreter_inode) +{ + int retval; + unsigned int 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_inode, 32, (char *) 0, + interp_ex->a_text+interp_ex->a_data); + } 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_inode, + N_TXTOFF(*interp_ex) , + (char *) N_TXTADDR(*interp_ex), + interp_ex->a_text+interp_ex->a_data); + } else + retval = -1; + + if(retval >= 0) + do_mmap(NULL, (interp_ex->a_text + interp_ex->a_data + 0xfff) & + 0xfffff000, interp_ex->a_bss, + PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_FIXED|MAP_PRIVATE, 0); + if(retval < 0) return 0xffffffff; + return elf_entry; +} + +/* + * These are the functions used to load ELF style executables and shared + * libraries. There is no binary dependent code anywhere else. + */ + +#define INTERPRETER_NONE 0 +#define INTERPRETER_AOUT 1 +#define INTERPRETER_ELF 2 + +static int load_elf_binary(struct linux_binprm * bprm, struct pt_regs * regs) +{ + struct elfhdr elf_ex; + struct elfhdr interp_elf_ex; + struct file * file; + struct exec interp_ex; + struct inode *interpreter_inode; + unsigned int load_addr; + unsigned int interpreter_type = INTERPRETER_NONE; + int i; + int old_fs; + int error; + struct elf_phdr * elf_ppnt, *elf_phdata; + int elf_exec_fileno; + unsigned int elf_bss, k, elf_brk; + int retval; + char * elf_interpreter; + unsigned int elf_entry; + int status; + unsigned int start_code, end_code, end_data; + unsigned int elf_stack; + char passed_fileno[6]; + + status = 0; + load_addr = 0; + elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ + + if (elf_ex.e_ident[0] != 0x7f || + strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) + return -ENOEXEC; + + + /* First of all, some simple consistency checks */ + if(elf_ex.e_type != ET_EXEC || + (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) || + (!bprm->inode->i_op || !bprm->inode->i_op->default_file_ops || + !bprm->inode->i_op->default_file_ops->mmap)){ + return -ENOEXEC; + }; + + /* Now read in all of the header information */ + + elf_phdata = (struct elf_phdr *) kmalloc(elf_ex.e_phentsize * + elf_ex.e_phnum, GFP_KERNEL); + + old_fs = get_fs(); + set_fs(get_ds()); + retval = read_exec(bprm->inode, elf_ex.e_phoff, (char *) elf_phdata, + elf_ex.e_phentsize * elf_ex.e_phnum); + set_fs(old_fs); + if (retval < 0) { + kfree (elf_phdata); + return retval; + } + + elf_ppnt = elf_phdata; + + elf_bss = 0; + elf_brk = 0; + + elf_exec_fileno = open_inode(bprm->inode, O_RDONLY); + + if (elf_exec_fileno < 0) { + kfree (elf_phdata); + return elf_exec_fileno; + } + + file = current->files->fd[elf_exec_fileno]; + + elf_stack = 0xffffffff; + elf_interpreter = NULL; + start_code = 0; + end_code = 0; + end_data = 0; + + old_fs = get_fs(); + set_fs(get_ds()); + + for(i=0;i < elf_ex.e_phnum; i++){ + if(elf_ppnt->p_type == PT_INTERP) { + /* This is the program interpreter used for shared libraries - + for now assume that this is an a.out format binary */ + + elf_interpreter = (char *) kmalloc(elf_ppnt->p_filesz, + GFP_KERNEL); + + retval = read_exec(bprm->inode,elf_ppnt->p_offset,elf_interpreter, + elf_ppnt->p_filesz); +#if 0 + printk("Using ELF interpreter %s\n", elf_interpreter); +#endif + if(retval >= 0) + retval = namei(elf_interpreter, &interpreter_inode); + if(retval >= 0) + retval = read_exec(interpreter_inode,0,bprm->buf,128); + + if(retval >= 0){ + interp_ex = *((struct exec *) bprm->buf); /* exec-header */ + interp_elf_ex = *((struct elfhdr *) bprm->buf); /* exec-header */ + + }; + if(retval < 0) { + kfree (elf_phdata); + kfree(elf_interpreter); + return retval; + }; + }; + elf_ppnt++; + }; + + set_fs(old_fs); + + /* Some simple consistency checks for the interpreter */ + if(elf_interpreter){ + interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT; + if(retval < 0) { + kfree(elf_interpreter); + kfree(elf_phdata); + return -ELIBACC; + }; + /* Now figure out which format our binary is */ + if((N_MAGIC(interp_ex) != OMAGIC) && + (N_MAGIC(interp_ex) != ZMAGIC) && + (N_MAGIC(interp_ex) != QMAGIC)) + interpreter_type = INTERPRETER_ELF; + + if (interp_elf_ex.e_ident[0] != 0x7f || + strncmp(&interp_elf_ex.e_ident[1], "ELF",3) != 0) + interpreter_type &= ~INTERPRETER_ELF; + + if(!interpreter_type) + { + kfree(elf_interpreter); + kfree(elf_phdata); + return -ELIBBAD; + }; + } + + /* OK, we are done with that, now set up the arg stuff, + and then start this sucker up */ + + if (!bprm->sh_bang) { + char * passed_p; + + if(interpreter_type == INTERPRETER_AOUT) { + sprintf(passed_fileno, "%d", elf_exec_fileno); + passed_p = passed_fileno; + + if(elf_interpreter) { + bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p,2); + bprm->argc++; + }; + }; + if (!bprm->p) { + if(elf_interpreter) { + kfree(elf_interpreter); + } + kfree (elf_phdata); + return -E2BIG; + } + } + + /* OK, This is the point of no return */ + flush_old_exec(bprm); + + current->mm->end_data = 0; + current->mm->end_code = 0; + current->mm->start_mmap = ELF_START_MMAP; + current->mm->mmap = NULL; + elf_entry = (unsigned int) elf_ex.e_entry; + + /* Do this so that we can load the interpreter, if need be. We will + change some of these later */ + current->mm->rss = 0; + bprm->p += change_ldt(0, bprm->page); + current->mm->start_stack = bprm->p; + + /* Now we do a little grungy work by mmaping the ELF image into + the correct location in memory. At this point, we assume that + the image should be loaded at fixed address, not at a variable + address. */ + + old_fs = get_fs(); + set_fs(get_ds()); + + elf_ppnt = elf_phdata; + for(i=0;i < elf_ex.e_phnum; i++){ + + if(elf_ppnt->p_type == PT_INTERP) { + /* Set these up so that we are able to load the interpreter */ + /* Now load the interpreter into user address space */ + set_fs(old_fs); + + if(interpreter_type & 1) elf_entry = + load_aout_interp(&interp_ex, interpreter_inode); + + if(interpreter_type & 2) elf_entry = + load_elf_interp(&interp_elf_ex, interpreter_inode); + + old_fs = get_fs(); + set_fs(get_ds()); + + iput(interpreter_inode); + kfree(elf_interpreter); + + if(elf_entry == 0xffffffff) { + printk("Unable to load interpreter\n"); + kfree(elf_phdata); + send_sig(SIGSEGV, current, 0); + return 0; + }; + }; + + + if(elf_ppnt->p_type == PT_LOAD) { + error = do_mmap(file, + elf_ppnt->p_vaddr & 0xfffff000, + elf_ppnt->p_filesz + (elf_ppnt->p_vaddr & 0xfff), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, + elf_ppnt->p_offset & 0xfffff000); + +#ifdef LOW_ELF_STACK + if(elf_ppnt->p_vaddr & 0xfffff000 < elf_stack) + elf_stack = elf_ppnt->p_vaddr & 0xfffff000; +#endif + + if(!load_addr) + load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset; + 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((elf_ppnt->p_flags | PROT_WRITE) && end_code < k) + end_code = k; + if(end_data < k) end_data = k; + k = elf_ppnt->p_vaddr + elf_ppnt->p_memsz; + if(k > elf_brk) elf_brk = k; + }; + elf_ppnt++; + }; + set_fs(old_fs); + + kfree(elf_phdata); + + if(interpreter_type != INTERPRETER_AOUT) sys_close(elf_exec_fileno); + + /* The following 3 lines need a little bit of work if we are loading + an iBCS2 binary. We should initially load it this way, and if + we get a lcall7, then we should look to see if the iBCS2 execution + profile is present. If it is, then switch to that, otherwise + bomb. */ + current->personality = PER_LINUX; + current->lcall7 = no_lcall7; + current->signal_map = current->signal_invmap = ident_map; + + current->executable = bprm->inode; + bprm->inode->i_count++; +#ifdef LOW_ELF_STACK + current->start_stack = p = elf_stack - 4; +#endif + bprm->p -= MAX_ARG_PAGES*PAGE_SIZE; + bprm->p = (unsigned long) + create_elf_tables((char *)bprm->p, + bprm->argc, + bprm->envc, + (interpreter_type == INTERPRETER_ELF ? &elf_ex : NULL), + load_addr, + (interpreter_type == INTERPRETER_AOUT ? 0 : 1)); + if(interpreter_type == INTERPRETER_AOUT) + 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; + current->suid = current->euid = bprm->e_uid; + current->sgid = current->egid = bprm->e_gid; + + /* Calling sys_brk effectively mmaps the pages that we need for the bss and break + sections */ + current->mm->brk = (elf_bss + 0xfff) & 0xfffff000; + sys_brk((elf_brk + 0xfff) & 0xfffff000); + + padzero(elf_bss); + + /* Why this, you ask??? Well SVr4 maps page 0 as read-only, + and some applications "depend" upon this behavior. + Since we do not have the power to recompile these, we + emulate the SVr4 behavior. Sigh. */ + error = do_mmap(NULL, 0, 4096, PROT_READ | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, 0); + + regs->eip = elf_entry; /* eip, magic happens :-) */ + regs->esp = bprm->p; /* stack pointer */ + if (current->flags & PF_PTRACED) + send_sig(SIGTRAP, current, 0); + return 0; +} + +/* This is really simpleminded and specialized - we are loading an + a.out library that is given an ELF header. */ + +static int load_elf_library(int fd){ + struct file * file; + struct elfhdr elf_ex; + struct elf_phdr *elf_phdata = NULL; + struct inode * inode; + unsigned int len; + int elf_bss; + int old_fs, retval; + unsigned int bss; + int error; + int i,j, k; + + len = 0; + file = current->files->fd[fd]; + inode = file->f_inode; + elf_bss = 0; + + set_fs(KERNEL_DS); + if (file->f_op->read(inode, file, (char *) &elf_ex, sizeof(elf_ex)) != sizeof(elf_ex)) { + sys_close(fd); + return -EACCES; + } + set_fs(USER_DS); + + if (elf_ex.e_ident[0] != 0x7f || + strncmp(&elf_ex.e_ident[1], "ELF",3) != 0) + return -ENOEXEC; + + /* First of all, some simple consistency checks */ + if(elf_ex.e_type != ET_EXEC || elf_ex.e_phnum > 2 || + (elf_ex.e_machine != EM_386 && elf_ex.e_machine != EM_486) || + (!inode->i_op || + !inode->i_op->default_file_ops->mmap)){ + return -ENOEXEC; + }; + + /* Now read in all of the header information */ + + if(sizeof(struct elf_phdr) * elf_ex.e_phnum > PAGE_SIZE) + return -ENOEXEC; + + elf_phdata = (struct elf_phdr *) + kmalloc(sizeof(struct elf_phdr) * elf_ex.e_phnum, GFP_KERNEL); + + old_fs = get_fs(); + set_fs(get_ds()); + retval = read_exec(inode, elf_ex.e_phoff, (char *) elf_phdata, + sizeof(struct elf_phdr) * elf_ex.e_phnum); + set_fs(old_fs); + + j = 0; + for(i=0; i<elf_ex.e_phnum; i++) + if((elf_phdata + i)->p_type == PT_LOAD) j++; + + if(j != 1) { + kfree(elf_phdata); + return -ENOEXEC; + }; + + while(elf_phdata->p_type != PT_LOAD) elf_phdata++; + + /* Now use mmap to map the library into memory. */ + error = do_mmap(file, + elf_phdata->p_vaddr & 0xfffff000, + elf_phdata->p_filesz + (elf_phdata->p_vaddr & 0xfff), + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_FIXED | MAP_PRIVATE, + elf_phdata->p_offset & 0xfffff000); + + k = elf_phdata->p_vaddr + elf_phdata->p_filesz; + if(k > elf_bss) elf_bss = k; + + sys_close(fd); + if (error != elf_phdata->p_vaddr & 0xfffff000) { + kfree(elf_phdata); + return error; + } + + padzero(elf_bss); + + len = (elf_phdata->p_filesz + elf_phdata->p_vaddr+ 0xfff) & 0xfffff000; + 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); + kfree(elf_phdata); + return 0; +} + +struct linux_binfmt elf_format = { NULL, load_elf_binary, load_elf_library }; diff --git a/ibcs/emulate.c b/ibcs/emulate.c new file mode 100644 index 000000000..dc3b3d576 --- /dev/null +++ b/ibcs/emulate.c @@ -0,0 +1,10 @@ +/* + * linux/abi/emulate.c + * + * Copyright (C) 1993 Linus Torvalds + */ + +/* + * Yes, sir, this file is completely empty, waiting for some real code.. + * I still copyright it, silly me. + */ |