Commit 7c4ee5bc authored by Richard Henderson's avatar Richard Henderson
Browse files

linux-user: Support stack-grows-up in elfload.c



HPPA is a (the) stack-grows-up target, and supporting that requires
rearranging how we compute addresses while laying out the initial
program stack.  In addition, hppa32 requires 64-byte stack alignment
so parameterize that as well.

Signed-off-by: default avatarRichard Henderson <rth@twiddle.net>
parent 429b31a2
Loading
Loading
Loading
Loading
+170 −65
Original line number Diff line number Diff line
@@ -1231,6 +1231,14 @@ static inline void init_thread(struct target_pt_regs *regs,
#define ELF_HWCAP 0
#endif

#ifndef STACK_GROWS_DOWN
#define STACK_GROWS_DOWN 1
#endif

#ifndef STACK_ALIGNMENT
#define STACK_ALIGNMENT 16
#endif

#ifdef TARGET_ABI32
#undef ELF_CLASS
#define ELF_CLASS ELFCLASS32
@@ -1374,17 +1382,17 @@ static abi_ulong copy_elf_strings(int argc, char **argv, char *scratch,
                                  abi_ulong p, abi_ulong stack_limit)
{
    char *tmp;
    int len, offset;
    int len, i;
    abi_ulong top = p;

    if (!p) {
        return 0;       /* bullet-proofing */
    }

    offset = ((p - 1) % TARGET_PAGE_SIZE) + 1;

    while (argc-- > 0) {
        tmp = argv[argc];
    if (STACK_GROWS_DOWN) {
        int offset = ((p - 1) % TARGET_PAGE_SIZE) + 1;
        for (i = argc - 1; i >= 0; --i) {
            tmp = argv[i];
            if (!tmp) {
                fprintf(stderr, "VFS: argc is wrong");
                exit(-1);
@@ -1411,9 +1419,42 @@ static abi_ulong copy_elf_strings(int argc, char **argv, char *scratch,
                }
            }
        }
    if (offset) {
        if (p != top) {
            memcpy_to_target(p, scratch + offset, top - p);
        }
    } else {
        int remaining = TARGET_PAGE_SIZE - (p % TARGET_PAGE_SIZE);
        for (i = 0; i < argc; ++i) {
            tmp = argv[i];
            if (!tmp) {
                fprintf(stderr, "VFS: argc is wrong");
                exit(-1);
            }
            len = strlen(tmp) + 1;
            if (len > (stack_limit - p)) {
                return 0;
            }
            while (len) {
                int bytes_to_copy = (len > remaining) ? remaining : len;

                memcpy_fromfs(scratch + (p - top), tmp, bytes_to_copy);

                tmp += bytes_to_copy;
                remaining -= bytes_to_copy;
                p += bytes_to_copy;
                len -= bytes_to_copy;

                if (remaining == 0) {
                    memcpy_to_target(top, scratch, p - top);
                    top = p;
                    remaining = TARGET_PAGE_SIZE;
                }
            }
        }
        if (p != top) {
            memcpy_to_target(top, scratch, p - top);
        }
    }

    return p;
}
@@ -1447,11 +1488,15 @@ static abi_ulong setup_arg_pages(struct linux_binprm *bprm,
    }

    /* We reserve one extra page at the top of the stack as guard.  */
    if (STACK_GROWS_DOWN) {
        target_mprotect(error, guard, PROT_NONE);

        info->stack_limit = error + guard;

        return info->stack_limit + size - sizeof(void *);
    } else {
        target_mprotect(error + size, guard, PROT_NONE);
        info->stack_limit = error + size;
        return error;
    }
}

/* Map and zero the bss.  We need to explicitly zero any fractional pages
@@ -1529,7 +1574,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
                                   struct image_info *interp_info)
{
    abi_ulong sp;
    abi_ulong sp_auxv;
    abi_ulong u_argc, u_argv, u_envp, u_auxv;
    int size;
    int i;
    abi_ulong u_rand_bytes;
@@ -1558,10 +1603,25 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
    k_platform = ELF_PLATFORM;
    if (k_platform) {
        size_t len = strlen(k_platform) + 1;
        if (STACK_GROWS_DOWN) {
            sp -= (len + n - 1) & ~(n - 1);
            u_platform = sp;
            /* FIXME - check return value of memcpy_to_target() for failure */
            memcpy_to_target(sp, k_platform, len);
        } else {
            memcpy_to_target(sp, k_platform, len);
            u_platform = sp;
            sp += len + 1;
        }
    }

    /* Provide 16 byte alignment for the PRNG, and basic alignment for
     * the argv and envp pointers.
     */
    if (STACK_GROWS_DOWN) {
        sp = QEMU_ALIGN_DOWN(sp, 16);
    } else {
        sp = QEMU_ALIGN_UP(sp, 16);
    }

    /*
@@ -1571,15 +1631,17 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
    for (i = 0; i < 16; i++) {
        k_rand_bytes[i] = rand();
    }
    if (STACK_GROWS_DOWN) {
        sp -= 16;
        u_rand_bytes = sp;
        /* FIXME - check return value of memcpy_to_target() for failure */
        memcpy_to_target(sp, k_rand_bytes, 16);
    } else {
        memcpy_to_target(sp, k_rand_bytes, 16);
        u_rand_bytes = sp;
        sp += 16;
    }

    /*
     * Force 16 byte _final_ alignment here for generality.
     */
    sp = sp &~ (abi_ulong)15;
    size = (DLINFO_ITEMS + 1) * 2;
    if (k_platform)
        size += 2;
@@ -1592,20 +1654,31 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
    size += envc + argc + 2;
    size += 1;  /* argc itself */
    size *= n;
    if (size & 15)
        sp -= 16 - (size & 15);

    /* Allocate space and finalize stack alignment for entry now.  */
    if (STACK_GROWS_DOWN) {
        u_argc = QEMU_ALIGN_DOWN(sp - size, STACK_ALIGNMENT);
        sp = u_argc;
    } else {
        u_argc = sp;
        sp = QEMU_ALIGN_UP(sp + size, STACK_ALIGNMENT);
    }

    u_argv = u_argc + n;
    u_envp = u_argv + (argc + 1) * n;
    u_auxv = u_envp + (envc + 1) * n;
    info->saved_auxv = u_auxv;
    info->arg_start = u_argv;
    info->arg_end = u_argv + argc * n;

    /* This is correct because Linux defines
     * elf_addr_t as Elf32_Off / Elf64_Off
     */
#define NEW_AUX_ENT(id, val) do {               \
        sp -= n; put_user_ual(val, sp);         \
        sp -= n; put_user_ual(id, sp);          \
        put_user_ual(id, u_auxv);  u_auxv += n; \
        put_user_ual(val, u_auxv); u_auxv += n; \
    } while(0)

    sp_auxv = sp;
    NEW_AUX_ENT (AT_NULL, 0);

    /* There must be exactly DLINFO_ITEMS entries here.  */
    NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff));
    NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr)));
@@ -1626,8 +1699,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
    NEW_AUX_ENT(AT_HWCAP2, (abi_ulong) ELF_HWCAP2);
#endif

    if (k_platform)
    if (u_platform) {
        NEW_AUX_ENT(AT_PLATFORM, u_platform);
    }
#ifdef ARCH_DLINFO
    /*
     * ARCH_DLINFO must come last so platform specific code can enforce
@@ -1635,14 +1709,29 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
     */
    ARCH_DLINFO;
#endif
    NEW_AUX_ENT (AT_NULL, 0);
#undef NEW_AUX_ENT

    info->saved_auxv = sp;
    info->auxv_len = sp_auxv - sp;
    info->auxv_len = u_argv - info->saved_auxv;

    put_user_ual(argc, u_argc);

    p = info->arg_strings;
    for (i = 0; i < argc; ++i) {
        put_user_ual(p, u_argv);
        u_argv += n;
        p += target_strlen(p) + 1;
    }
    put_user_ual(0, u_argv);

    p = info->env_strings;
    for (i = 0; i < envc; ++i) {
        put_user_ual(p, u_envp);
        u_envp += n;
        p += target_strlen(p) + 1;
    }
    put_user_ual(0, u_envp);

    sp = loader_build_argptr(envc, argc, sp, p, 0);
    /* Check the right amount of stack was allocated for auxvec, envp & argv. */
    assert(sp_auxv - sp == size);
    return sp;
}

@@ -2213,12 +2302,28 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
    bprm->p = setup_arg_pages(bprm, info);

    scratch = g_new0(char, TARGET_PAGE_SIZE);
    if (STACK_GROWS_DOWN) {
        bprm->p = copy_elf_strings(1, &bprm->filename, scratch,
                                   bprm->p, info->stack_limit);
        info->file_string = bprm->p;
        bprm->p = copy_elf_strings(bprm->envc, bprm->envp, scratch,
                                   bprm->p, info->stack_limit);
        info->env_strings = bprm->p;
        bprm->p = copy_elf_strings(bprm->argc, bprm->argv, scratch,
                                   bprm->p, info->stack_limit);
        info->arg_strings = bprm->p;
    } else {
        info->arg_strings = bprm->p;
        bprm->p = copy_elf_strings(bprm->argc, bprm->argv, scratch,
                                   bprm->p, info->stack_limit);
        info->env_strings = bprm->p;
        bprm->p = copy_elf_strings(bprm->envc, bprm->envp, scratch,
                                   bprm->p, info->stack_limit);
        info->file_string = bprm->p;
        bprm->p = copy_elf_strings(1, &bprm->filename, scratch,
                                   bprm->p, info->stack_limit);
    }

    g_free(scratch);

    if (!bprm->p) {
+7 −6
Original line number Diff line number Diff line
@@ -4179,15 +4179,16 @@ int main(int argc, char **argv, char **envp)

        qemu_log("start_brk   0x" TARGET_ABI_FMT_lx "\n", info->start_brk);
        qemu_log("end_code    0x" TARGET_ABI_FMT_lx "\n", info->end_code);
        qemu_log("start_code  0x" TARGET_ABI_FMT_lx "\n",
                 info->start_code);
        qemu_log("start_data  0x" TARGET_ABI_FMT_lx "\n",
                 info->start_data);
        qemu_log("start_code  0x" TARGET_ABI_FMT_lx "\n", info->start_code);
        qemu_log("start_data  0x" TARGET_ABI_FMT_lx "\n", info->start_data);
        qemu_log("end_data    0x" TARGET_ABI_FMT_lx "\n", info->end_data);
        qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n",
                 info->start_stack);
        qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n", info->start_stack);
        qemu_log("brk         0x" TARGET_ABI_FMT_lx "\n", info->brk);
        qemu_log("entry       0x" TARGET_ABI_FMT_lx "\n", info->entry);
        qemu_log("argv_start  0x" TARGET_ABI_FMT_lx "\n", info->arg_start);
        qemu_log("env_start   0x" TARGET_ABI_FMT_lx "\n",
                 info->arg_end + (abi_ulong)sizeof(abi_ulong));
        qemu_log("auxv_start  0x" TARGET_ABI_FMT_lx "\n", info->saved_auxv);
    }

    target_set_brk(info->brk);
+3 −0
Original line number Diff line number Diff line
@@ -48,6 +48,9 @@ struct image_info {
        abi_ulong       auxv_len;
        abi_ulong       arg_start;
        abi_ulong       arg_end;
        abi_ulong       arg_strings;
        abi_ulong       env_strings;
        abi_ulong       file_string;
        uint32_t        elf_flags;
	int		personality;
#ifdef CONFIG_USE_FDPIC