This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Fix interrupt.exp fails with m32 in x86_64


make check RUNTESTFLAGS="GDB_TESTCASE_OPTIONS=\"-m32\" interrupt.exp" in
x86_64 will got some fails:
FAIL: gdb.base/interrupt.exp: signal SIGINT (the program is no longer running)
FAIL: gdb.base/interrupt.exp: echo more data (timeout)
FAIL: gdb.base/interrupt.exp: send end of file
The issue can be reproduced:
#uname -p
x86_64

#gcc -g -m32 gdb.base/interrupt.c
#gdb ./a.out
(gdb) r
Starting program: /home/teawater/gdb/binutils-gdb/gdb/testsuite/a.out
talk to me baby
data
data
^C
Program received signal SIGINT, Interrupt.
0xf7ffd430 in __kernel_vsyscall ()
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0xf7ffd430 in __kernel_vsyscall ()
(gdb) p func1()
$1 = 4
(gdb) c
Continuing.
Unknown error 512
[Inferior 1 (process 7953) exited with code 01]

      nbytes = read (0, &x, 1);
      if (nbytes < 0)
    {
#ifdef EINTR
      if (errno != EINTR)
#endif
After GDB call a function "func1()" by hands, "read" will get
errno 512(ERESTARTSYS) that should handled by Linux kernel.

The root cause of this issue is:
When user use ctrl-c stop the inferior, the signal will be handled in
Linux kernel function "do_signal" in arch/x86/kernel/signal.c.
The inferior will be stoped by function "ptrace_stop".  The call trace is:
#0  freezable_schedule () at include/linux/freezer.h:172
#1  ptrace_stop (exit_code=exit_code@entry=5, why=why@entry=262148,
    clear_code=clear_code@entry=0, info=info@entry=0xffff88001d833e78)
    at kernel/signal.c:1920
#2  0xffffffff8107ec33 in ptrace_signal (info=0xffff88001d833e78, signr=5)
    at kernel/signal.c:2157
#3  get_signal_to_deliver (info=info@entry=0xffff88001d833e78,
    return_ka=return_ka@entry=0xffff88001d833e58, regs=<optimized out>,
    cookie=cookie@entry=0x0 <irq_stack_union>) at kernel/signal.c:2269
#4  0xffffffff81013438 in do_signal (regs=regs@entry=0xffff88001d833f58)
    at arch/x86/kernel/signal.c:696
#5  0xffffffff81013a40 in do_notify_resume (regs=0xffff88001d833f58,
unused=<optimized out>, thread_info_flags=4) at arch/x86/kernel/signal.c:747
#6  <signal handler called>
#7  0x0000000000000000 in irq_stack_union ()

When GDB "call func1()", to control inferior execute the function func1()
and go back to old ip.  GDB need set all the registers by GDB function
"amd64_collect_native_gregset" that will zero-extend most of 32 bits registers
to 64 bits and set to inferior.
And execute from ptrace_stop and got back to do_signal.
current_thread_info()->status TS_COMPAT will be clean by function
"int_with_check" when it return to user space.

When GDB "continue", inferior will execute from ptrace_stop and got back
to do_signal again.
Because this signal interrupt a syscall, go back to function do_signal
will use function "syscall_get_error" check if this is a syscall and got
error:
static inline long syscall_get_error(struct task_struct *task,
                     struct pt_regs *regs)
{
    unsigned long error = regs->ax;
#ifdef CONFIG_IA32_EMULATION
    /*
     * TS_COMPAT is set for 32-bit syscall entries and then
     * remains set until we return to user mode.
     */
    if (task_thread_info(task)->status & TS_COMPAT)
        /*
         * Sign-extend the value so (int)-EFOO becomes (long)-EFOO
         * and will match correctly in comparisons.
         */
        error = (long) (int) error;
#endif
    return IS_ERR_VALUE(error) ? error : 0;
}
Now ax is in 32 bits now, need sign-extend to 64 bits.  But
current_thread_info()->status TS_COMPAT is cleared when GDB call "call func1()".
Linux kernel don't know this is a 32 bits task and will not extend it.
Then -ERESTARTSYS is not be handled and go back to user space.

Then the syscall "read" get a errno in ERESTARTSYS.

I make a patch that let eax sign-extend in function amd64_collect_native_gregset
that can handle this issue.
It can handle the issue and pass the regression test.
Please help me review it.

And I make a another patch for Linux kernel that can handle this issue
too.  I will post it to LKML later.

Thanks,
Hui

2014-04-21  Hui Zhu  <hui@codesourcery.com>

    * amd64-nat.c(amd64_collect_native_gregset): Make %eax sign-extended.
--- a/gdb/amd64-nat.c
+++ b/gdb/amd64-nat.c
@@ -131,10 +131,12 @@ amd64_collect_native_gregset (const stru
     {
       num_regs = amd64_native_gregset32_num_regs;

-      /* Make sure %eax, %ebx, %ecx, %edx, %esi, %edi, %ebp, %esp and
+      /* Make sure %ebx, %ecx, %edx, %esi, %edi, %ebp, %esp and
          %eip get zero-extended to 64 bits.  */
       for (i = 0; i <= I386_EIP_REGNUM; i++)
     {
+      if (i == I386_EAX_REGNUM)
+        continue;
       if (regnum == -1 || regnum == i)
memset (regs + amd64_native_gregset_reg_offset (gdbarch, i), 0, 8);
     }
@@ -156,7 +158,24 @@ amd64_collect_native_gregset (const stru
       int offset = amd64_native_gregset_reg_offset (gdbarch, i);

       if (offset != -1)
-        regcache_raw_collect (regcache, i, regs + offset);
+        {
+          if (i == I386_EAX_REGNUM
+          && gdbarch_bfd_arch_info (gdbarch)->bits_per_word == 32)
+            {
+          /* Make sure %eax get sign-extended to 64 bits. */
+          LONGEST val;
+
+          regcache_raw_collect (regcache, I386_EAX_REGNUM,
+                    regs + offset);
+          val = extract_signed_integer ((gdb_byte *)(regs + offset),
+                        4,
+                        gdbarch_byte_order (gdbarch));
+          store_signed_integer ((gdb_byte *)(regs + offset), 8,
+                    gdbarch_byte_order (gdbarch), val);
+        }
+          else
+        regcache_raw_collect (regcache, i, regs + offset);
+        }
     }
     }
 }


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]