int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs,

                    struct image_info * info)

{

    struct elfhdr elf_ex;

    struct elfhdr interp_elf_ex;

    struct exec interp_ex;

    int interpreter_fd = -1; /* avoid warning */

    abi_ulong load_addr, load_bias;

    int load_addr_set = 0;

    unsigned int interpreter_type = INTERPRETER_NONE;

    unsigned char ibcs2_interpreter;

    int i;

    abi_ulong mapped_addr;

    struct elf_phdr * elf_ppnt;

    struct elf_phdr *elf_phdata;

    abi_ulong k, elf_brk;

    int retval;

    char * elf_interpreter;

    abi_ulong elf_entry, interp_load_addr = 0;

    int status;

    abi_ulong start_code, end_code, start_data, end_data;

    abi_ulong reloc_func_desc = 0;

    abi_ulong elf_stack;

    char passed_fileno[6];



    ibcs2_interpreter = 0;

    status = 0;

    load_addr = 0;

    load_bias = 0;

    elf_ex = *((struct elfhdr *) bprm->buf);          /* exec-header */

    bswap_ehdr(&elf_ex);



    /* First of all, some simple consistency checks */

    if ((elf_ex.e_type != ET_EXEC && elf_ex.e_type != ET_DYN) ||

        (! elf_check_arch(elf_ex.e_machine))) {

        return -ENOEXEC;

    }



    bprm->p = copy_elf_strings(1, &bprm->filename, bprm->page, bprm->p);

    bprm->p = copy_elf_strings(bprm->envc,bprm->envp,bprm->page,bprm->p);

    bprm->p = copy_elf_strings(bprm->argc,bprm->argv,bprm->page,bprm->p);

    if (!bprm->p) {

        retval = -E2BIG;

    }



    /* Now read in all of the header information */

    elf_phdata = (struct elf_phdr *)malloc(elf_ex.e_phentsize*elf_ex.e_phnum);

    if (elf_phdata == NULL) {

        return -ENOMEM;

    }



    i = elf_ex.e_phnum * sizeof(struct elf_phdr);

    if (elf_ex.e_phoff + i <= BPRM_BUF_SIZE) {

        memcpy(elf_phdata, bprm->buf + elf_ex.e_phoff, i);

    } else {

        retval = pread(bprm->fd, (char *) elf_phdata, i, elf_ex.e_phoff);

        if (retval != i) {

            perror("load_elf_binary");

            exit(-1);

        }

    }

    bswap_phdr(elf_phdata, elf_ex.e_phnum);



    elf_brk = 0;

    elf_stack = ~((abi_ulong)0UL);

    elf_interpreter = NULL;

    start_code = ~((abi_ulong)0UL);

    end_code = 0;

    start_data = 0;

    end_data = 0;

    interp_ex.a_info = 0;



    elf_ppnt = elf_phdata;

    for(i=0;i < elf_ex.e_phnum; i++) {

        if (elf_ppnt->p_type == PT_INTERP) {

            if ( elf_interpreter != NULL )

            {

                free (elf_phdata);

                free(elf_interpreter);

                close(bprm->fd);

                return -EINVAL;

            }



            /* This is the program interpreter used for

             * shared libraries - for now assume that this

             * is an a.out format binary

             */



            elf_interpreter = (char *)malloc(elf_ppnt->p_filesz);



            if (elf_interpreter == NULL) {

                free (elf_phdata);

                close(bprm->fd);

                return -ENOMEM;

            }



            if (elf_ppnt->p_offset + elf_ppnt->p_filesz <= BPRM_BUF_SIZE) {

                memcpy(elf_interpreter, bprm->buf + elf_ppnt->p_offset,

                       elf_ppnt->p_filesz);

            } else {

                retval = pread(bprm->fd, elf_interpreter, elf_ppnt->p_filesz,

                               elf_ppnt->p_offset);

                if (retval != elf_ppnt->p_filesz) {

                    perror("load_elf_binary2");

                    exit(-1);

                }

            }



            /* If the program interpreter is one of these two,

               then assume an iBCS2 image. Otherwise assume

               a native linux image. */



            /* JRP - Need to add X86 lib dir stuff here... */



            if (strcmp(elf_interpreter,"/usr/lib/libc.so.1") == 0 ||

                strcmp(elf_interpreter,"/usr/lib/ld.so.1") == 0) {

                ibcs2_interpreter = 1;

            }



            retval = open(path(elf_interpreter), O_RDONLY);

            if (retval < 0) {

                perror(elf_interpreter);

                exit(-1);

            }

            interpreter_fd = retval;



            retval = read(interpreter_fd, bprm->buf, BPRM_BUF_SIZE);

            if (retval < 0) {

                perror("load_elf_binary3");

                exit(-1);

            }

            if (retval < BPRM_BUF_SIZE) {

                memset(bprm->buf, 0, BPRM_BUF_SIZE - retval);

            }



            interp_ex = *((struct exec *) bprm->buf); /* aout exec-header */

            interp_elf_ex = *((struct elfhdr *) bprm->buf); /* elf exec-header */

        }

        elf_ppnt++;

    }



    /* Some simple consistency checks for the interpreter */

    if (elf_interpreter){

        interpreter_type = INTERPRETER_ELF | INTERPRETER_AOUT;



        /* 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((char *)&interp_elf_ex.e_ident[1], "ELF",3) != 0) {

            interpreter_type &= ~INTERPRETER_ELF;

        }



        if (!interpreter_type) {

            free(elf_interpreter);

            free(elf_phdata);

            close(bprm->fd);

            return -ELIBBAD;

        }

    }



    /* OK, we are done with that, now set up the arg stuff,

       and then start this sucker up */



    {

        char * passed_p;



        if (interpreter_type == INTERPRETER_AOUT) {

            snprintf(passed_fileno, sizeof(passed_fileno), "%d", bprm->fd);

            passed_p = passed_fileno;



            if (elf_interpreter) {

                bprm->p = copy_elf_strings(1,&passed_p,bprm->page,bprm->p);

                bprm->argc++;

            }

        }

        if (!bprm->p) {

            if (elf_interpreter) {

                free(elf_interpreter);

            }

            free (elf_phdata);

            close(bprm->fd);

            return -E2BIG;

        }

    }



    /* OK, This is the point of no return */

    info->end_data = 0;

    info->end_code = 0;

    info->start_mmap = (abi_ulong)ELF_START_MMAP;

    info->mmap = 0;

    elf_entry = (abi_ulong) elf_ex.e_entry;



#if defined(CONFIG_USE_GUEST_BASE)

    /*

     * In case where user has not explicitly set the guest_base, we

     * probe here that should we set it automatically.

     */

    if (!(have_guest_base || reserved_va)) {

        /*

         * Go through ELF program header table and find the address

         * range used by loadable segments.  Check that this is available on

         * the host, and if not find a suitable value for guest_base.  */

        abi_ulong app_start = ~0;

        abi_ulong app_end = 0;

        abi_ulong addr;

        unsigned long host_start;

        unsigned long real_start;

        unsigned long host_size;

        for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum;

             i++, elf_ppnt++) {

            if (elf_ppnt->p_type != PT_LOAD)

                continue;

            addr = elf_ppnt->p_vaddr;

            if (addr < app_start) {

                app_start = addr;

            }

            addr += elf_ppnt->p_memsz;

            if (addr > app_end) {

                app_end = addr;

            }

        }



        /* If we don't have any loadable segments then something

           is very wrong.  */

        assert(app_start < app_end);



        /* Round addresses to page boundaries.  */

        app_start = app_start & qemu_host_page_mask;

        app_end = HOST_PAGE_ALIGN(app_end);

        if (app_start < mmap_min_addr) {

            host_start = HOST_PAGE_ALIGN(mmap_min_addr);

        } else {

            host_start = app_start;

            if (host_start != app_start) {

                fprintf(stderr, "qemu: Address overflow loading ELF binary\n");

                abort();

            }

        }

        host_size = app_end - app_start;

        while (1) {

            /* Do not use mmap_find_vma here because that is limited to the

               guest address space.  We are going to make the

               guest address space fit whatever we're given.  */

            real_start = (unsigned long)mmap((void *)host_start, host_size,

                PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);

            if (real_start == (unsigned long)-1) {

                fprintf(stderr, "qemu: Virtual memory exausted\n");

                abort();

            }

            if (real_start == host_start) {

                break;

            }

            /* That address didn't work.  Unmap and try a different one.

               The address the host picked because is typically

               right at the top of the host address space and leaves the

               guest with no usable address space.  Resort to a linear search.

               We already compensated for mmap_min_addr, so this should not

               happen often.  Probably means we got unlucky and host address

               space randomization put a shared library somewhere

               inconvenient.  */

            munmap((void *)real_start, host_size);

            host_start += qemu_host_page_size;

            if (host_start == app_start) {

                /* Theoretically possible if host doesn't have any

                   suitably aligned areas.  Normally the first mmap will

                   fail.  */

                fprintf(stderr, "qemu: Unable to find space for application\n");

                abort();

            }

        }

        qemu_log("Relocating guest address space from 0x" TARGET_ABI_FMT_lx

                 " to 0x%lx\n", app_start, real_start);

        guest_base = real_start - app_start;

    }

#endif /* CONFIG_USE_GUEST_BASE */



    /* Do this so that we can load the interpreter, if need be.  We will

       change some of these later */

    info->rss = 0;

    bprm->p = setup_arg_pages(bprm->p, bprm, info);

    info->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.

     */



    for(i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; i++, elf_ppnt++) {

        int elf_prot = 0;

        int elf_flags = 0;

        abi_ulong error;



        if (elf_ppnt->p_type != PT_LOAD)

            continue;



        if (elf_ppnt->p_flags & PF_R) elf_prot |= PROT_READ;

        if (elf_ppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;

        if (elf_ppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;

        elf_flags = MAP_PRIVATE | MAP_DENYWRITE;

        if (elf_ex.e_type == ET_EXEC || load_addr_set) {

            elf_flags |= MAP_FIXED;

        } else if (elf_ex.e_type == ET_DYN) {

            /* Try and get dynamic programs out of the way of the default mmap

               base, as well as whatever program they might try to exec.  This

               is because the brk will follow the loader, and is not movable.  */

            /* NOTE: for qemu, we do a big mmap to get enough space

               without hardcoding any address */

            error = target_mmap(0, ET_DYN_MAP_SIZE,

                                PROT_NONE, MAP_PRIVATE | MAP_ANON,

                                -1, 0);

            if (error == -1) {

                perror("mmap");

                exit(-1);

            }

            load_bias = TARGET_ELF_PAGESTART(error - elf_ppnt->p_vaddr);

        }



        error = target_mmap(TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr),

                            (elf_ppnt->p_filesz +

                             TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)),

                            elf_prot,

                            (MAP_FIXED | MAP_PRIVATE | MAP_DENYWRITE),

                            bprm->fd,

                            (elf_ppnt->p_offset -

                             TARGET_ELF_PAGEOFFSET(elf_ppnt->p_vaddr)));

        if (error == -1) {

            perror("mmap");

            exit(-1);

        }



#ifdef LOW_ELF_STACK

        if (TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr) < elf_stack)

            elf_stack = TARGET_ELF_PAGESTART(elf_ppnt->p_vaddr);

#endif



        if (!load_addr_set) {

            load_addr_set = 1;

            load_addr = elf_ppnt->p_vaddr - elf_ppnt->p_offset;

            if (elf_ex.e_type == ET_DYN) {

                load_bias += error -

                    TARGET_ELF_PAGESTART(load_bias + elf_ppnt->p_vaddr);

                load_addr += load_bias;

                reloc_func_desc = load_bias;

            }

        }

        k = elf_ppnt->p_vaddr;

        if (k < start_code)

            start_code = k;

        if (start_data < k)

            start_data = k;

        k = elf_ppnt->p_vaddr + elf_ppnt->p_filesz;

        if ((elf_ppnt->p_flags & PF_X) && 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 = TARGET_PAGE_ALIGN(k);

        }



        /* If the load segment requests extra zeros (e.g. bss), map it.  */

        if (elf_ppnt->p_filesz < elf_ppnt->p_memsz) {

            abi_ulong base = load_bias + elf_ppnt->p_vaddr;

            zero_bss(base + elf_ppnt->p_filesz,

                     base + elf_ppnt->p_memsz, elf_prot);

        }

    }



    elf_entry += load_bias;

    elf_brk += load_bias;

    start_code += load_bias;

    end_code += load_bias;

    start_data += load_bias;

    end_data += load_bias;



    if (elf_interpreter) {

        if (interpreter_type & 1) {

            elf_entry = load_aout_interp(&interp_ex, interpreter_fd);

        } else if (interpreter_type & 2) {

            elf_entry = load_elf_interp(&interp_elf_ex, interpreter_fd,

                                        &interp_load_addr, bprm->buf);

        }

        reloc_func_desc = interp_load_addr;



        close(interpreter_fd);

        free(elf_interpreter);



        if (elf_entry == ~((abi_ulong)0UL)) {

            printf("Unable to load interpreter\n");

            free(elf_phdata);

            exit(-1);

            return 0;

        }

    }



    free(elf_phdata);



    if (qemu_log_enabled()) {

        load_symbols(&elf_ex, bprm->fd, load_bias);

    }



    if (interpreter_type != INTERPRETER_AOUT) close(bprm->fd);

    info->personality = (ibcs2_interpreter ? PER_SVR4 : PER_LINUX);



#ifdef LOW_ELF_STACK

    info->start_stack = bprm->p = elf_stack - 4;

#endif

    bprm->p = create_elf_tables(bprm->p,

                                bprm->argc,

                                bprm->envc,

                                &elf_ex,

                                load_addr, load_bias,

                                interp_load_addr,

                                (interpreter_type == INTERPRETER_AOUT ? 0 : 1),

                                info);

    info->load_addr = reloc_func_desc;

    info->start_brk = info->brk = elf_brk;

    info->end_code = end_code;

    info->start_code = start_code;

    info->start_data = start_data;

    info->end_data = end_data;

    info->start_stack = bprm->p;



#if 0

    printf("(start_brk) %x\n" , info->start_brk);

    printf("(end_code) %x\n" , info->end_code);

    printf("(start_code) %x\n" , info->start_code);

    printf("(end_data) %x\n" , info->end_data);

    printf("(start_stack) %x\n" , info->start_stack);

    printf("(brk) %x\n" , info->brk);

#endif



    if ( info->personality == PER_SVR4 )

    {

        /* 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.  */

        mapped_addr = target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC,

                                  MAP_FIXED | MAP_PRIVATE, -1, 0);

    }



    info->entry = elf_entry;



#ifdef USE_ELF_CORE_DUMP

    bprm->core_dump = &elf_core_dump;

#endif



    return 0;

}
