diff options
Diffstat (limited to 'linux-user/elfload.c')
-rw-r--r-- | linux-user/elfload.c | 168 |
1 files changed, 65 insertions, 103 deletions
diff --git a/linux-user/elfload.c b/linux-user/elfload.c index fe7a5bc566..4563a3190b 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -354,7 +354,6 @@ enum { /* The commpage only exists for 32 bit kernels */ -#define TARGET_HAS_VALIDATE_GUEST_SPACE /* Return 1 if the proposed guest space is suitable for the guest. * Return 0 if the proposed guest space isn't suitable, but another * address space should be tried. @@ -363,8 +362,8 @@ enum { * The guest code may leave a page mapped and populate it if the * address is suitable. */ -static int validate_guest_space(unsigned long guest_base, - unsigned long guest_size) +static int init_guest_commpage(unsigned long guest_base, + unsigned long guest_size) { unsigned long real_start, test_page_addr; @@ -375,6 +374,11 @@ static int validate_guest_space(unsigned long guest_base, /* If the commpage lies within the already allocated guest space, * then there is no way we can allocate it. + * + * You may be thinking that that this check is redundant because + * we already validated the guest size against MAX_RESERVED_VA; + * but if qemu_host_page_mask is unusually large, then + * test_page_addr may be lower. */ if (test_page_addr >= guest_base && test_page_addr < (guest_base + guest_size)) { @@ -563,78 +567,6 @@ static uint32_t get_elf_hwcap(void) #endif /* not TARGET_AARCH64 */ #endif /* TARGET_ARM */ -#ifdef TARGET_UNICORE32 - -#define ELF_START_MMAP 0x80000000 - -#define ELF_CLASS ELFCLASS32 -#define ELF_DATA ELFDATA2LSB -#define ELF_ARCH EM_UNICORE32 - -static inline void init_thread(struct target_pt_regs *regs, - struct image_info *infop) -{ - abi_long stack = infop->start_stack; - memset(regs, 0, sizeof(*regs)); - regs->UC32_REG_asr = 0x10; - regs->UC32_REG_pc = infop->entry & 0xfffffffe; - regs->UC32_REG_sp = infop->start_stack; - /* FIXME - what to for failure of get_user()? */ - get_user_ual(regs->UC32_REG_02, stack + 8); /* envp */ - get_user_ual(regs->UC32_REG_01, stack + 4); /* envp */ - /* XXX: it seems that r0 is zeroed after ! */ - regs->UC32_REG_00 = 0; -} - -#define ELF_NREG 34 -typedef target_elf_greg_t target_elf_gregset_t[ELF_NREG]; - -static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUUniCore32State *env) -{ - (*regs)[0] = env->regs[0]; - (*regs)[1] = env->regs[1]; - (*regs)[2] = env->regs[2]; - (*regs)[3] = env->regs[3]; - (*regs)[4] = env->regs[4]; - (*regs)[5] = env->regs[5]; - (*regs)[6] = env->regs[6]; - (*regs)[7] = env->regs[7]; - (*regs)[8] = env->regs[8]; - (*regs)[9] = env->regs[9]; - (*regs)[10] = env->regs[10]; - (*regs)[11] = env->regs[11]; - (*regs)[12] = env->regs[12]; - (*regs)[13] = env->regs[13]; - (*regs)[14] = env->regs[14]; - (*regs)[15] = env->regs[15]; - (*regs)[16] = env->regs[16]; - (*regs)[17] = env->regs[17]; - (*regs)[18] = env->regs[18]; - (*regs)[19] = env->regs[19]; - (*regs)[20] = env->regs[20]; - (*regs)[21] = env->regs[21]; - (*regs)[22] = env->regs[22]; - (*regs)[23] = env->regs[23]; - (*regs)[24] = env->regs[24]; - (*regs)[25] = env->regs[25]; - (*regs)[26] = env->regs[26]; - (*regs)[27] = env->regs[27]; - (*regs)[28] = env->regs[28]; - (*regs)[29] = env->regs[29]; - (*regs)[30] = env->regs[30]; - (*regs)[31] = env->regs[31]; - - (*regs)[32] = cpu_asr_read((CPUUniCore32State *)env); - (*regs)[33] = env->regs[0]; /* XXX */ -} - -#define USE_ELF_CORE_DUMP -#define ELF_EXEC_PAGESIZE 4096 - -#define ELF_HWCAP (UC32_HWCAP_CMOV | UC32_HWCAP_UCF64) - -#endif - #ifdef TARGET_SPARC #ifdef TARGET_SPARC64 @@ -1927,21 +1859,12 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, return sp; } -#ifndef TARGET_HAS_VALIDATE_GUEST_SPACE -/* If the guest doesn't have a validation function just agree */ -static int validate_guest_space(unsigned long guest_base, - unsigned long guest_size) -{ - return 1; -} -#endif - unsigned long init_guest_space(unsigned long host_start, unsigned long host_size, unsigned long guest_start, bool fixed) { - unsigned long current_start, real_start; + unsigned long current_start, aligned_start; int flags; assert(host_start || host_size); @@ -1949,11 +1872,12 @@ unsigned long init_guest_space(unsigned long host_start, /* If just a starting address is given, then just verify that * address. */ if (host_start && !host_size) { - if (validate_guest_space(host_start, host_size) == 1) { - return host_start; - } else { +#if defined(TARGET_ARM) && !defined(TARGET_AARCH64) + if (init_guest_commpage(host_start, host_size) != 1) { return (unsigned long)-1; } +#endif + return host_start; } /* Setup the initial flags and start address. */ @@ -1966,7 +1890,8 @@ unsigned long init_guest_space(unsigned long host_start, /* Otherwise, a non-zero size region of memory needs to be mapped * and validated. */ while (1) { - unsigned long real_size = host_size; + unsigned long real_start, real_size, aligned_size; + aligned_size = real_size = host_size; /* Do not use mmap_find_vma here because that is limited to the * guest address space. We are going to make the @@ -1978,30 +1903,63 @@ unsigned long init_guest_space(unsigned long host_start, return (unsigned long)-1; } + /* Check to see if the address is valid. */ + if (host_start && real_start != current_start) { + goto try_again; + } + /* Ensure the address is properly aligned. */ if (real_start & ~qemu_host_page_mask) { + /* Ideally, we adjust like + * + * pages: [ ][ ][ ][ ][ ] + * old: [ real ] + * [ aligned ] + * new: [ real ] + * [ aligned ] + * + * But if there is something else mapped right after it, + * then obviously it won't have room to grow, and the + * kernel will put the new larger real someplace else with + * unknown alignment (if we made it to here, then + * fixed=false). Which is why we grow real by a full page + * size, instead of by part of one; so that even if we get + * moved, we can still guarantee alignment. But this does + * mean that there is a padding of < 1 page both before + * and after the aligned range; the "after" could could + * cause problems for ARM emulation where it could butt in + * to where we need to put the commpage. + */ munmap((void *)real_start, host_size); - real_size = host_size + qemu_host_page_size; + real_size = aligned_size + qemu_host_page_size; real_start = (unsigned long) mmap((void *)real_start, real_size, PROT_NONE, flags, -1, 0); if (real_start == (unsigned long)-1) { return (unsigned long)-1; } - real_start = HOST_PAGE_ALIGN(real_start); + aligned_start = HOST_PAGE_ALIGN(real_start); + } else { + aligned_start = real_start; } - /* Check to see if the address is valid. */ - if (!host_start || real_start == current_start) { - int valid = validate_guest_space(real_start - guest_start, - real_size); - if (valid == 1) { - break; - } else if (valid == -1) { - return (unsigned long)-1; - } - /* valid == 0, so try again. */ +#if defined(TARGET_ARM) && !defined(TARGET_AARCH64) + /* On 32-bit ARM, we need to also be able to map the commpage. */ + int valid = init_guest_commpage(aligned_start - guest_start, + aligned_size + guest_start); + if (valid == -1) { + munmap((void *)real_start, real_size); + return (unsigned long)-1; + } else if (valid == 0) { + goto try_again; } +#endif + + /* If nothing has said `return -1` or `goto try_again` yet, + * then the address we have is good. + */ + break; + try_again: /* 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 @@ -2010,8 +1968,12 @@ unsigned long init_guest_space(unsigned long host_start, * happen often. Probably means we got unlucky and host * address space randomization put a shared library somewhere * inconvenient. + * + * This is probably a good strategy if host_start, but is + * probably a bad strategy if not, which means we got here + * because of trouble with ARM commpage setup. */ - munmap((void *)real_start, host_size); + munmap((void *)real_start, real_size); current_start += qemu_host_page_size; if (host_start == current_start) { /* Theoretically possible if host doesn't have any suitably @@ -2023,7 +1985,7 @@ unsigned long init_guest_space(unsigned long host_start, qemu_log_mask(CPU_LOG_PAGE, "Reserved 0x%lx bytes of guest address space\n", host_size); - return real_start; + return aligned_start; } static void probe_guest_base(const char *image_name, |