Messages in this thread | | | From | Hui Zhu <> | Date | Thu, 12 Jun 2014 16:40:47 +0800 | Subject | Re: [PATCH v2] Fix get ERESTARTSYS with m32 in x86_64 when debug by GDB |
| |
Ping.
Thanks, Hui
On Mon, May 5, 2014 at 8:00 PM, Hui Zhu <hui@codesourcery.com> wrote: > cat gdb.base/interrupt.c > #include <errno.h> > #include <stdio.h> > #include <unistd.h> > #include <stdlib.h> > > #ifdef SIGNALS > #include <signal.h> > > static void > sigint_handler (int signo) > { > } > #endif > > int > main () > { > char x; > int nbytes; > #ifdef SIGNALS > signal (SIGINT, sigint_handler); > #endif > printf ("talk to me baby\n"); > while (1) > { > nbytes = read (0, &x, 1); > if (nbytes < 0) > { > #ifdef EINTR > if (errno != EINTR) > #endif > { > perror (""); > return 1; > } > } > else if (nbytes == 0) > { > printf ("end of file\n"); > exit (0); > } > else > write (1, &x, 1); > } > return 0; > } > > int > func1 () > { > return 4; > } > 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] > > The root cause is: > When inferior call 32 bits syscall "read", Linux kernel function > "ia32_cstar_target" will set TS_COMPAT to current_thread_info->status. > > syscall read is interrupt by ctrl-c. Then the $rax will be set to > errno -512 in 64 bits. > And the inferior will be stopped by Linux kernel 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 () > > 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). > Step 2, change the values of registers. > Step 3, Push a dummy frame to stack. > Step 4, set a breakpint in the return address. > > When GDB resume the inferior, it will keep execut from ptrace_stop > with new values of registers that set by GDB. > And TS_COMPAT inside current_thread_info->status will be cleared when > inferior switch back to user space. > > When function "func1()" return, inferior will be stoped by breakpoint > inferior will be stopped by Linux kernel function "ptrace_stop" again. > current_thread_info->status will not set TS_COMPAT when inferior swith > from user space to kernel space because breakpoint handler "int3" doesn't > has code for that. > > GDB begin to set saved values of registers back to inferior that use > function "amd64_collect_native_gregset". Because this function just > zero-extend each 32 bits value to 64 bits value before put them to inferior. > $rax's value is set to 0xfffffe00(32 bits -512) but not > 0xfffffffffffffe00(64 bits -512). > > When GDB continue syscall "read" that is interrupted by "ctrl-c", it will > keep execute from ptrace_stop without "TS_COMPAT". > Then in Linux kernel function "syscall_get_error", current_thread_info->status > doesn't have TS_COMPAT and $rax is 0xfffffe00(32 bits -512). Then in > function do_signal will not handle this -ERESTARTSYS. > > -ERESTARTSYS will be return back to inferior, that is why inferior got a > errno -ERESTARTSYS. > > According to comments from Mark. I make a new version that add following > code to putreg(): > case offsetof(struct user_regs_struct, orig_ax): > /* > * A 64-bit debugger setting orig_ax of a 32-bit inferior > * means to restore the state of the task restarting a > * 32-bit syscall. > * Make sure we interpret the -ERESTART* codes correctly > * in case the task is not actually still sitting at the > * exit from a 32-bit syscall with TS_COMPAT still set. > */ > if (test_ti_thread_flag(task_thread_info(child), TIF_IA32)) { > struct pt_regs *regs = task_pt_regs(child); > regs->orig_ax = value; > if (syscall_get_nr(child, regs) >= 0) > task_thread_info(child)->status |= TS_COMPAT; > return 0; > } > break; > > Signed-off-by: Hui Zhu <hui@codesourcery.com> > --- > --- a/arch/x86/kernel/ptrace.c > +++ b/arch/x86/kernel/ptrace.c > @@ -452,6 +452,23 @@ static int putreg(struct task_struct *ch > if (child->thread.gs != value) > return do_arch_prctl(child, ARCH_SET_GS, value); > return 0; > + case offsetof(struct user_regs_struct, orig_ax): > + /* > + * A 64-bit debugger setting orig_ax of a 32-bit inferior > + * means to restore the state of the task restarting a > + * 32-bit syscall. > + * Make sure we interpret the -ERESTART* codes correctly > + * in case the task is not actually still sitting at the > + * exit from a 32-bit syscall with TS_COMPAT still set. > + */ > + if (test_ti_thread_flag(task_thread_info(child), TIF_IA32)) { > + struct pt_regs *regs = task_pt_regs(child); > + regs->orig_ax = value; > + if (syscall_get_nr(child, regs) >= 0) > + task_thread_info(child)->status |= TS_COMPAT; > + return 0; > + } > + break; > #endif > } >
| |