aboutsummaryrefslogtreecommitdiff
path: root/linux-user/elfload.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux-user/elfload.c')
-rw-r--r--linux-user/elfload.c168
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,