Re: [PATCH v3 2/3] x86/signal: Rewire the restart_block() syscall to have a constant nr

From: Pedro Alves
Date: Wed Jun 22 2016 - 08:00:35 EST


On 06/21/2016 05:32 PM, Andy Lutomirski wrote:
> On Jun 21, 2016 5:40 AM, "Pedro Alves" <palves@xxxxxxxxxx> wrote:

> I didn't try that particular experiment. But, from that email:
>
>> After that, GDB can control the stopped inferior. To call function "func1()" of inferior, GDB need: Step 1, save current values of registers ($rax 0xfffffffffffffe00(64 bits -512) is cut to 0xfffffe00(32 bits -512) because inferior is a 32 bits program).
>
> That sounds like it may be a gdb bug. Why does gdb truncate the register?

Because when debugging a 32-bit program, gdb's register cache only
stores 32-bit-wide registers ($eax, $eip, etc., not $rax, etc.)

Let me turn this around:

Why does the kernel care about the upper 32-bit bits of $orig_rax when
the task is in 32-bit mode in the first place?

The 32-bit syscall entry points already only care about $eax, not $rax,
since $rax doesn't exist in real 32-bit CPUs.

Looking at arch/x86/entry/entry_64_compat.S, all three 64-bit x 32-bit syscall
entry points zero-extend $eax already, and then push that as pt_regs->orig_ax.

So if the kernel is giving significance to the higher 32-bits of orig_ax at
32-bit syscall restart time, that very much looks like a kernel bug to me.

>
> I haven't played with it recently, but, in my experience, gdb seems to
> work quite poorly in mixed-mode situations. For example, if you
> attach 64-bit gdb to qemu-system-x86_64's gdbserver, boot a 64-bit
> guest, and breakpoint in early 32-bit code, gdb tends to explode
> pretty badly.

Right, but that's a bit of a red herring, and not entirely gdb's fault. The case you
mention happens because qemu does exactly the opposite of what you're suggesting below.
It's qemu that changes the remote protocol's register layout (and thus size) in the
remote protocol register read/write packets when the kernel changes mode, behind
gdb's back, and gdb errors out because obviously it isn't expecting that. All gdb
knows is that qemu is now sending bogus register read replies, different from what
the target description qemu reported on initial remote connection described.

I say "entirely", because gdb has its own share of fault for the remote protocol
not including a some kind of standard mechanism to inform gdb of mode changes.

However, the usual scenario where the program _doesn't_ change mode
during execution, is supported.

>
> On x86_64, I think gdb should treat CPU state as 64-bit no matter
> what. The fact that a 32-bit tracee code segment is in use shouldn't
> change much.

It's not as clear or easy as you make it sound, unfortunately.

For normal userspace programs, the current design across
gdb/remote protocol/ptrace/kernel/core dump format/elf formats/ is that
what matters is the program's architecture, not whatever the tracer's arch
is.

Should core dumping dump 64-bit CPU state as well for 32-bit programs?
The current core dump format dumps a 32-bit elf with notes that contain
32-bit registers. And I think it'd be a bit odd for a 32-bit program to
dump different cores files depending on the bitness of the kernel.

Should a gdb connected to a 64-bit gdbserver that is debugging a 32-bit
program see different registers compared to a gdb that
is connected to a 32-bit gdbserver that is debugging a 32-bit program?
Currently, it doesn't. The architecture of gdbserver doesn't matter here,
only the tracee's.

> Admittedly the kernel doesn't really help. There is some questionable
> code involving which regsets to show to ptrace.

I don't know what code you're looking at, but I consider this mandatory reading:

Roland McGrath on PTRACE_GETREGSET design:
https://sourceware.org/ml/archer/2010-q3/msg00193.html

"A caveat about those requests for bi-arch systems. Unlike other
ptrace requests, these access the native formats of the tracee
process, rather than the native formats of the debugger process.
So, a 64-bit debugger process using PTRACE_GETREGSET on a 32-bit
tracee process will see the 32-bit layouts (i.e. what would appear
in an ELF core file if that process dumped one)."


gdb currently uses PTRACE_SETREGS for the general registers, which
means it currently writes those as 64-bit registers. However, if gdb or any
ptracer restores/writes $eax/$orig_eax using PTRACE_SETREGSET, it's only
going to pass down a 32-bit value, and again it must be the kernel that sign
extends $orig_eax if it wants to interpret it as signed 64-bit internally.


But actually looking at your patch 3, I'm confused, because it seems
to be doing what I'm suggesting?


Thanks,
Pedro Alves