diff mbox

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

Message ID 1498582198-6649-1-git-send-email-peter.maydell@linaro.org
State New
Headers show

Commit Message

Peter Maydell June 27, 2017, 4:49 p.m. UTC
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
7c4ee5bcc82e64, 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>
---
 linux-user/elfload.c | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

Comments

Richard Henderson June 27, 2017, 6:05 p.m. UTC | #1
On 06/27/2017 09:49 AM, Peter Maydell wrote:
> 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
> 7c4ee5bcc82e64, 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>
> ---
>   linux-user/elfload.c | 23 ++++++++++++-----------
>   1 file changed, 12 insertions(+), 11 deletions(-)

Reviewed-by: Richard Henderson <rth@twiddle.net>
Tested-by:  Richard Henderson <rth@twiddle.net>


r~
Peter Maydell June 29, 2017, 10:44 a.m. UTC | #2
On 27 June 2017 at 19:05, Richard Henderson <rth@twiddle.net> wrote:
> On 06/27/2017 09:49 AM, Peter Maydell wrote:
>>
>> 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
>> 7c4ee5bcc82e64, 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>
>> ---
>>   linux-user/elfload.c | 23 ++++++++++++-----------
>>   1 file changed, 12 insertions(+), 11 deletions(-)
>
>
> Reviewed-by: Richard Henderson <rth@twiddle.net>
> Tested-by:  Richard Henderson <rth@twiddle.net>

Thanks; applied directly to master since this has been
causing my mergebuild tests to fail (some recent environment
change result in it triggering this week...)

thanks
-- PMM
diff mbox

Patch

diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index ce77317..2a902f7 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -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