X-Git-Url: http://vcs.maemo.org/git/?a=blobdiff_plain;f=linux-user%2Felfload.c;h=c0ea5a01256116ba02b952ef69b97b4b0d07b086;hb=c35734b2a6f9b028edacd5813ff271728ce2a9e3;hp=29730dca781a4a4fab70947c7ed63a1af9264b25;hpb=0240ded8bb1580147ed2ff1748df439a3b41e38f;p=qemu diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 29730dc..c0ea5a0 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -3,7 +3,6 @@ #include #include #include -#include #include #include #include @@ -59,19 +58,19 @@ static uint32_t get_elf_hwcap(void) #define ELF_DATA ELFDATA2LSB #define ELF_ARCH EM_386 - /* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program - starts %edx contains a pointer to a function which might be - registered using `atexit'. This provides a mean for the - dynamic linker to call DT_FINI functions for shared libraries - that have been loaded before the code runs. - - A value of 0 tells we have no such handler. */ -#define ELF_PLAT_INIT(_r) _r->edx = 0 - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->esp = infop->start_stack; regs->eip = infop->entry; + + /* SVR4/i386 ABI (pages 3-31, 3-32) says that when the program + starts %edx contains a pointer to a function which might be + registered using `atexit'. This provides a mean for the + dynamic linker to call DT_FINI functions for shared libraries + that have been loaded before the code runs. + + A value of 0 tells we have no such handler. */ + regs->edx = 0; } #define USE_ELF_CORE_DUMP @@ -93,21 +92,21 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i #endif #define ELF_ARCH EM_ARM -#define ELF_PLAT_INIT(_r) _r->ARM_r0 = 0 - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { - target_long *stack = (void *)infop->start_stack; + target_long stack = infop->start_stack; memset(regs, 0, sizeof(*regs)); regs->ARM_cpsr = 0x10; if (infop->entry & 1) regs->ARM_cpsr |= CPSR_T; regs->ARM_pc = infop->entry & 0xfffffffe; regs->ARM_sp = infop->start_stack; - regs->ARM_r2 = tswapl(stack[2]); /* envp */ - regs->ARM_r1 = tswapl(stack[1]); /* argv */ + regs->ARM_r2 = tgetl(stack + 8); /* envp */ + regs->ARM_r1 = tgetl(stack + 4); /* envp */ /* XXX: it seems that r0 is zeroed after ! */ - // regs->ARM_r0 = tswapl(stack[0]); /* argc */ + regs->ARM_r0 = 0; + /* For uClinux PIC binaries. */ + regs->ARM_r10 = infop->start_data; } #define USE_ELF_CORE_DUMP @@ -136,14 +135,13 @@ enum #define ELF_START_MMAP 0x80000000 -#define elf_check_arch(x) ( (x) == EM_SPARC ) +#define elf_check_arch(x) ( (x) == EM_SPARCV9 ) #define ELF_CLASS ELFCLASS64 #define ELF_DATA ELFDATA2MSB -#define ELF_ARCH EM_SPARC +#define ELF_ARCH EM_SPARCV9 -/*XXX*/ -#define ELF_PLAT_INIT(_r) +#define STACK_BIAS 2047 static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -151,7 +149,7 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i regs->pc = infop->entry; regs->npc = regs->pc + 4; regs->y = 0; - regs->u_regs[14] = infop->start_stack - 16 * 4; + regs->u_regs[14] = infop->start_stack - 16 * 8 - STACK_BIAS; } #else @@ -163,9 +161,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i #define ELF_DATA ELFDATA2MSB #define ELF_ARCH EM_SPARC -/*XXX*/ -#define ELF_PLAT_INIT(_r) - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->psr = 0; @@ -192,20 +187,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i #endif #define ELF_ARCH EM_PPC -/* Note that isn't exactly what regular kernel does - * but this is what the ABI wants and is needed to allow - * execution of PPC BSD programs. - */ -#define ELF_PLAT_INIT(_r) \ -do { \ - target_ulong *pos = (target_ulong *)bprm->p, tmp = 1; \ - _r->gpr[3] = bprm->argc; \ - _r->gpr[4] = (unsigned long)++pos; \ - for (; tmp != 0; pos++) \ - tmp = *pos; \ - _r->gpr[5] = (unsigned long)pos; \ -} while (0) - /* * We need to put in some extra aux table entries to tell glibc what * the cache block size is, so it can use the dcbz instruction safely. @@ -239,9 +220,22 @@ do { \ static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop) { + target_ulong pos = infop->start_stack; + target_ulong tmp; + _regs->msr = 1 << MSR_PR; /* Set user mode */ _regs->gpr[1] = infop->start_stack; _regs->nip = infop->entry; + /* Note that isn't exactly what regular kernel does + * but this is what the ABI wants and is needed to allow + * execution of PPC BSD programs. + */ + _regs->gpr[3] = tgetl(pos); + pos += sizeof(target_ulong); + _regs->gpr[4] = pos; + for (tmp = 1; tmp != 0; pos += sizeof(target_ulong)) + tmp = ldl(pos); + _regs->gpr[5] = pos; } #define USE_ELF_CORE_DUMP @@ -263,8 +257,6 @@ static inline void init_thread(struct target_pt_regs *_regs, struct image_info * #endif #define ELF_ARCH EM_MIPS -#define ELF_PLAT_INIT(_r) - static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { regs->cp0_status = CP0St_UM; @@ -274,6 +266,53 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i #endif /* TARGET_MIPS */ +#ifdef TARGET_SH4 + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_SH ) + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2LSB +#define ELF_ARCH EM_SH + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + /* Check other registers XXXXX */ + regs->pc = infop->entry; + regs->regs[15] = infop->start_stack - 16 * 4; +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 4096 + +#endif + +#ifdef TARGET_M68K + +#define ELF_START_MMAP 0x80000000 + +#define elf_check_arch(x) ( (x) == EM_68K ) + +#define ELF_CLASS ELFCLASS32 +#define ELF_DATA ELFDATA2MSB +#define ELF_ARCH EM_68K + +/* ??? Does this need to do anything? +#define ELF_PLAT_INIT(_r) */ + +static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) +{ + regs->usp = infop->start_stack; + regs->sr = 0; + regs->pc = infop->entry; +} + +#define USE_ELF_CORE_DUMP +#define ELF_EXEC_PAGESIZE 8192 + +#endif + #ifndef ELF_PLATFORM #define ELF_PLATFORM (NULL) #endif @@ -284,30 +323,6 @@ static inline void init_thread(struct target_pt_regs *regs, struct image_info *i #include "elf.h" -/* - * MAX_ARG_PAGES defines the number of pages allocated for arguments - * and envelope for the new program. 32 should suffice, this gives - * a maximum env+arg of 128kB w/4KB pages! - */ -#define MAX_ARG_PAGES 32 - -/* - * This structure is used to hold the arguments that are - * used when loading binaries. - */ -struct linux_binprm { - char buf[128]; - unsigned long page[MAX_ARG_PAGES]; - unsigned long p; - int sh_bang; - int fd; - int e_uid, e_gid; - int argc, envc; - char * filename; /* Name of binary */ - unsigned long loader, exec; - int dont_iput; /* binfmt handler has put inode */ -}; - struct exec { unsigned int a_info; /* Use macros N_MAGIC, etc for access */ @@ -353,8 +368,6 @@ struct exec #define PER_XENIX (0x0007 | STICKY_TIMEOUTS) /* Necessary parameters */ -#define NGROUPS 32 - #define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE #define TARGET_ELF_PAGESTART(_v) ((_v) & ~(unsigned long)(TARGET_ELF_EXEC_PAGESIZE-1)) #define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1)) @@ -427,38 +440,14 @@ static void bswap_sym(Elf32_Sym *sym) } #endif -static void * get_free_page(void) -{ - void * retval; - - /* User-space version of kernel get_free_page. Returns a page-aligned - * page-sized chunk of memory. - */ - retval = (void *)target_mmap(0, qemu_host_page_size, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - - if((long)retval == -1) { - perror("get_free_page"); - exit(-1); - } - else { - return(retval); - } -} - -static void free_page(void * pageaddr) -{ - target_munmap((unsigned long)pageaddr, qemu_host_page_size); -} - /* - * 'copy_string()' copies argument/envelope strings from user + * 'copy_elf_strings()' copies argument/envelope strings from user * memory to free pages in kernel mem. These are in a format ready * to be put directly into the top of new user memory. * */ -static unsigned long copy_strings(int argc,char ** argv,unsigned long *page, - unsigned long p) +static unsigned long copy_elf_strings(int argc,char ** argv, void **page, + unsigned long p) { char *tmp, *tmp1, *pag = NULL; int len, offset = 0; @@ -482,10 +471,10 @@ static unsigned long copy_strings(int argc,char ** argv,unsigned long *page, --p; --tmp; --len; if (--offset < 0) { offset = p % TARGET_PAGE_SIZE; - pag = (char *) page[p/TARGET_PAGE_SIZE]; + pag = (char *)page[p/TARGET_PAGE_SIZE]; if (!pag) { - pag = (char *)get_free_page(); - page[p/TARGET_PAGE_SIZE] = (unsigned long)pag; + pag = (char *)malloc(TARGET_PAGE_SIZE); + page[p/TARGET_PAGE_SIZE] = pag; if (!pag) return 0; } @@ -506,95 +495,10 @@ static unsigned long copy_strings(int argc,char ** argv,unsigned long *page, return p; } -static int in_group_p(gid_t g) -{ - /* return TRUE if we're in the specified group, FALSE otherwise */ - int ngroup; - int i; - gid_t grouplist[NGROUPS]; - - ngroup = getgroups(NGROUPS, grouplist); - for(i = 0; i < ngroup; i++) { - if(grouplist[i] == g) { - return 1; - } - } - return 0; -} - -static int count(char ** vec) -{ - int i; - - for(i = 0; *vec; i++) { - vec++; - } - - return(i); -} - -static int prepare_binprm(struct linux_binprm *bprm) -{ - struct stat st; - int mode; - int retval, id_change; - - if(fstat(bprm->fd, &st) < 0) { - return(-errno); - } - - mode = st.st_mode; - if(!S_ISREG(mode)) { /* Must be regular file */ - return(-EACCES); - } - if(!(mode & 0111)) { /* Must have at least one execute bit set */ - return(-EACCES); - } - - bprm->e_uid = geteuid(); - bprm->e_gid = getegid(); - id_change = 0; - - /* Set-uid? */ - if(mode & S_ISUID) { - bprm->e_uid = st.st_uid; - if(bprm->e_uid != geteuid()) { - id_change = 1; - } - } - - /* Set-gid? */ - /* - * If setgid is set but no group execute bit then this - * is a candidate for mandatory locking, not a setgid - * executable. - */ - if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { - bprm->e_gid = st.st_gid; - if (!in_group_p(bprm->e_gid)) { - id_change = 1; - } - } - - memset(bprm->buf, 0, sizeof(bprm->buf)); - retval = lseek(bprm->fd, 0L, SEEK_SET); - if(retval >= 0) { - retval = read(bprm->fd, bprm->buf, 128); - } - if(retval < 0) { - perror("prepare_binprm"); - exit(-1); - /* return(-errno); */ - } - else { - return(retval); - } -} - -unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm, - struct image_info * info) +unsigned long setup_arg_pages(target_ulong p, struct linux_binprm * bprm, + struct image_info * info) { - unsigned long stack_base, size, error; + target_ulong stack_base, size, error; int i; /* Create enough stack to hold everything. If we don't use @@ -618,19 +522,14 @@ unsigned long setup_arg_pages(unsigned long p, struct linux_binprm * bprm, stack_base = error + size - MAX_ARG_PAGES*TARGET_PAGE_SIZE; p += stack_base; - if (bprm->loader) { - bprm->loader += stack_base; - } - bprm->exec += stack_base; - for (i = 0 ; i < MAX_ARG_PAGES ; i++) { if (bprm->page[i]) { info->rss++; - memcpy((void *)stack_base, (void *)bprm->page[i], TARGET_PAGE_SIZE); - free_page((void *)bprm->page[i]); + memcpy_to_target(stack_base, bprm->page[i], TARGET_PAGE_SIZE); + free(bprm->page[i]); } - stack_base += TARGET_PAGE_SIZE; + stack_base += TARGET_PAGE_SIZE; } return p; } @@ -654,10 +553,12 @@ static void set_brk(unsigned long start, unsigned long end) /* 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(unsigned long elf_bss) +static void padzero(unsigned long elf_bss, unsigned long last_bss) { unsigned long nbyte; - char * fpnt; + + if (elf_bss >= last_bss) + return; /* XXX: this is really a hack : if the real host page size is smaller than the target page size, some pages after the end @@ -679,55 +580,56 @@ static void padzero(unsigned long elf_bss) nbyte = elf_bss & (qemu_host_page_size-1); if (nbyte) { nbyte = qemu_host_page_size - nbyte; - fpnt = (char *) elf_bss; do { - *fpnt++ = 0; + tput8(elf_bss, 0); + elf_bss++; } while (--nbyte); } } -static unsigned int * create_elf_tables(char *p, int argc, int envc, - struct elfhdr * exec, - unsigned long load_addr, - unsigned long load_bias, - unsigned long interp_load_addr, int ibcs, - struct image_info *info) + +static unsigned long create_elf_tables(target_ulong p, int argc, int envc, + struct elfhdr * exec, + unsigned long load_addr, + unsigned long load_bias, + unsigned long interp_load_addr, int ibcs, + struct image_info *info) { - target_ulong *argv, *envp; - target_ulong *sp, *csp; - target_ulong *u_platform; + target_ulong sp; + int size; + target_ulong u_platform; const char *k_platform; - int v; + const int n = sizeof(target_ulong); - /* - * Force 16 byte _final_ alignment here for generality. - */ - sp = (unsigned int *) (~15UL & (unsigned long) p); - u_platform = NULL; + sp = p; + u_platform = 0; k_platform = ELF_PLATFORM; if (k_platform) { size_t len = strlen(k_platform) + 1; - sp -= (len + sizeof(target_ulong) - 1) / sizeof(target_ulong); - u_platform = (target_ulong *)sp; - __copy_to_user(u_platform, k_platform, len); + sp -= (len + n - 1) & ~(n - 1); + u_platform = sp; + memcpy_to_target(sp, k_platform, len); } - csp = sp; - csp -= (DLINFO_ITEMS + 1) * 2; + /* + * Force 16 byte _final_ alignment here for generality. + */ + sp = sp &~ (target_ulong)15; + size = (DLINFO_ITEMS + 1) * 2; if (k_platform) - csp -= 2; + size += 2; #ifdef DLINFO_ARCH_ITEMS - csp -= DLINFO_ARCH_ITEMS*2; + size += DLINFO_ARCH_ITEMS * 2; #endif - csp -= envc+1; - csp -= argc+1; - csp -= (!ibcs ? 3 : 1); /* argc itself */ - if ((unsigned long)csp & 15UL) - sp -= ((unsigned long)csp & 15UL) / sizeof(*sp); + size += envc + argc + 2; + size += (!ibcs ? 3 : 1); /* argc itself */ + size *= n; + if (size & 15) + sp -= 16 - (size & 15); -#define NEW_AUX_ENT(id, val) \ - sp -= 2; \ - put_user (id, sp); \ - put_user (val, sp + 1) +#define NEW_AUX_ENT(id, val) do { \ + sp -= n; tputl(sp, val); \ + sp -= n; tputl(sp, id); \ + } while(0) NEW_AUX_ENT (AT_NULL, 0); /* There must be exactly DLINFO_ITEMS entries here. */ @@ -744,7 +646,7 @@ static unsigned int * create_elf_tables(char *p, int argc, int envc, NEW_AUX_ENT(AT_EGID, (target_ulong) getegid()); NEW_AUX_ENT(AT_HWCAP, (target_ulong) ELF_HWCAP); if (k_platform) - NEW_AUX_ENT(AT_PLATFORM, (target_ulong) u_platform); + NEW_AUX_ENT(AT_PLATFORM, u_platform); #ifdef ARCH_DLINFO /* * ARCH_DLINFO must come last so platform specific code can enforce @@ -754,39 +656,11 @@ static unsigned int * create_elf_tables(char *p, int argc, int envc, #endif #undef NEW_AUX_ENT - sp -= envc+1; - envp = sp; - sp -= argc+1; - argv = sp; - if (!ibcs) { - put_user((target_ulong)envp,--sp); - put_user((target_ulong)argv,--sp); - } - put_user(argc,--sp); - info->arg_start = (unsigned int)((unsigned long)p & 0xffffffff); - while (argc-->0) { - put_user((target_ulong)p,argv++); - do { - get_user(v, p); - p++; - } while (v != 0); - } - put_user(0,argv); - info->arg_end = info->env_start = (unsigned int)((unsigned long)p & 0xffffffff); - while (envc-->0) { - put_user((target_ulong)p,envp++); - do { - get_user(v, p); - p++; - } while (v != 0); - } - put_user(0,envp); - info->env_end = (unsigned int)((unsigned long)p & 0xffffffff); + sp = loader_build_argptr(envc, argc, sp, p, !ibcs); return sp; } - static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, int interpreter_fd, unsigned long *interp_load_addr) @@ -890,7 +764,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, interpreter_fd, eppnt->p_offset - TARGET_ELF_PAGEOFFSET(eppnt->p_vaddr)); - if (error > -1024UL) { + if (error == -1) { /* Real error */ close(interpreter_fd); free(elf_phdata); @@ -927,7 +801,7 @@ static unsigned long load_elf_interp(struct elfhdr * interp_elf_ex, * that there are zeromapped pages up to and including the last * bss page. */ - padzero(elf_bss); + padzero(elf_bss, last_bss); elf_bss = TARGET_ELF_PAGESTART(elf_bss + qemu_host_page_size - 1); /* What we have mapped so far */ /* Map the last of the bss segment */ @@ -997,8 +871,8 @@ static void load_symbols(struct elfhdr *hdr, int fd) syminfos = s; } -static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, - struct image_info * info) +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; @@ -1030,17 +904,19 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r bswap_ehdr(&elf_ex); #endif - 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_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) { @@ -1184,7 +1060,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r /* 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) { @@ -1192,7 +1068,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r passed_p = passed_fileno; if (elf_interpreter) { - bprm->p = copy_strings(1,&passed_p,bprm->page,bprm->p); + bprm->p = copy_elf_strings(1,&passed_p,bprm->page,bprm->p); bprm->argc++; } } @@ -1335,8 +1211,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r #ifdef LOW_ELF_STACK info->start_stack = bprm->p = elf_stack - 4; #endif - bprm->p = (unsigned long) - create_elf_tables((char *)bprm->p, + bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex, @@ -1344,11 +1219,10 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r interp_load_addr, (interpreter_type == INTERPRETER_AOUT ? 0 : 1), info); - if (interpreter_type == INTERPRETER_AOUT) - info->arg_start += strlen(passed_fileno) + 1; info->start_brk = info->brk = elf_brk; info->end_code = end_code; info->start_code = start_code; + info->start_data = end_code; info->end_data = end_data; info->start_stack = bprm->p; @@ -1356,7 +1230,7 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r sections */ set_brk(elf_bss, elf_brk); - padzero(elf_bss); + padzero(elf_bss, elf_brk); #if 0 printf("(start_brk) %x\n" , info->start_brk); @@ -1377,78 +1251,18 @@ static int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * r MAP_FIXED | MAP_PRIVATE, -1, 0); } -#ifdef ELF_PLAT_INIT - /* - * The ABI may specify that certain registers be set up in special - * ways (on i386 %edx is the address of a DT_FINI function, for - * example. This macro performs whatever initialization to - * the regs structure is required. - */ - ELF_PLAT_INIT(regs); -#endif - - info->entry = elf_entry; return 0; } - - -int elf_exec(const char * filename, char ** argv, char ** envp, - struct target_pt_regs * regs, struct image_info *infop) -{ - struct linux_binprm bprm; - int retval; - int i; - - bprm.p = TARGET_PAGE_SIZE*MAX_ARG_PAGES-sizeof(unsigned int); - for (i=0 ; i=0) { - bprm.p = copy_strings(1, &bprm.filename, bprm.page, bprm.p); - bprm.exec = bprm.p; - bprm.p = copy_strings(bprm.envc,envp,bprm.page,bprm.p); - bprm.p = copy_strings(bprm.argc,argv,bprm.page,bprm.p); - if (!bprm.p) { - retval = -E2BIG; - } - } - - if(retval>=0) { - retval = load_elf_binary(&bprm,regs,infop); - } - if(retval>=0) { - /* success. Initialize important registers */ - init_thread(regs, infop); - return retval; - } - - /* Something went wrong, return the inode and free the argument pages*/ - for (i=0 ; i