linux-user: Put PPC AT_IGNOREPPC auxv entries in the right place

The 32-bit PPC auxv is a bit complicated because in the
mists of time it used to be 16-aligned rather than directly
after the environment. Older glibc versions had code to
try to probe for whether it needed alignment or not:
https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/unix/sysv/linux/powerpc/dl-sysdep.c;hb=e84eabb3871c9b39e59323bf3f6b98c2ca9d1cd0
and the kernel has code which puts some magic entries at
the bottom to ensure that the alignment probe fails:
http://elixir.free-electrons.com/linux/latest/source/arch/powerpc/include/asm/elf.h#L158

QEMU has similar code too, but it was broken by commit
7c4ee5bcc8, which changed elfload.c from filling in
the auxv starting at the highest address and working down
to starting at the lowest address and working up. This
means that the ARCH_DLINFO hook must now be invoked first
rather than last, and the entries in it for PPC must
be reversed so that the magic AT_IGNOREPPC entries come
at the lowest address in the auxv as they should.

The effect of this was that if running a guest binary that
used an old glibc with the alignment probing the guest ld.so
code would segfault if the size of the guest environment and
argv happened to put the auxv at an address that triggered
the alignment code in the guest glibc.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <rth@twiddle.net>
Tested-by: Richard Henderson <rth@twiddle.net>
Message-id: 1498582198-6649-1-git-send-email-peter.maydell@linaro.org
This commit is contained in:
Peter Maydell 2017-06-27 17:49:58 +01:00
parent 577caa2672
commit 82991bed73

View File

@ -802,14 +802,15 @@ static uint32_t get_elf_hwcap2(void)
#define ARCH_DLINFO \
do { \
PowerPCCPU *cpu = POWERPC_CPU(thread_cpu); \
NEW_AUX_ENT(AT_DCACHEBSIZE, cpu->env.dcache_line_size); \
NEW_AUX_ENT(AT_ICACHEBSIZE, cpu->env.icache_line_size); \
NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \
/* \
* Now handle glibc compatibility. \
* Handle glibc compatibility: these magic entries must \
* be at the lowest addresses in the final auxv. \
*/ \
NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
NEW_AUX_ENT(AT_IGNOREPPC, AT_IGNOREPPC); \
NEW_AUX_ENT(AT_DCACHEBSIZE, cpu->env.dcache_line_size); \
NEW_AUX_ENT(AT_ICACHEBSIZE, cpu->env.icache_line_size); \
NEW_AUX_ENT(AT_UCACHEBSIZE, 0); \
} while (0)
static inline void init_thread(struct target_pt_regs *_regs, struct image_info *infop)
@ -1760,6 +1761,13 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
} while(0)
/* There must be exactly DLINFO_ITEMS entries here. */
#ifdef ARCH_DLINFO
/*
* ARCH_DLINFO must come first so platform specific code can enforce
* special alignment requirements on the AUXV if necessary (eg. PPC).
*/
ARCH_DLINFO;
#endif
NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff));
NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr)));
NEW_AUX_ENT(AT_PHNUM, (abi_ulong)(exec->e_phnum));
@ -1782,13 +1790,6 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc,
if (u_platform) {
NEW_AUX_ENT(AT_PLATFORM, u_platform);
}
#ifdef ARCH_DLINFO
/*
* ARCH_DLINFO must come last so platform specific code can enforce
* special alignment requirements on the AUXV if necessary (eg. PPC).
*/
ARCH_DLINFO;
#endif
NEW_AUX_ENT (AT_NULL, 0);
#undef NEW_AUX_ENT