From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 11470 invoked by alias); 28 Feb 2012 13:43:14 -0000 Received: (qmail 11458 invoked by uid 22791); 28 Feb 2012 13:43:11 -0000 X-SWARE-Spam-Status: No, hits=-6.6 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_HI,SPF_HELO_PASS,TW_EG,TW_XC,T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from mx1.redhat.com (HELO mx1.redhat.com) (209.132.183.28) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 28 Feb 2012 13:42:50 +0000 Received: from int-mx02.intmail.prod.int.phx2.redhat.com (int-mx02.intmail.prod.int.phx2.redhat.com [10.5.11.12]) by mx1.redhat.com (8.14.4/8.14.4) with ESMTP id q1SDglxJ010614 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Tue, 28 Feb 2012 08:42:47 -0500 Received: from host2.jankratochvil.net (ovpn-116-19.ams2.redhat.com [10.36.116.19]) by int-mx02.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id q1SDgfgS007257 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NO); Tue, 28 Feb 2012 08:42:44 -0500 Date: Tue, 28 Feb 2012 13:55:00 -0000 From: Jan Kratochvil To: Yao Qi Cc: gdb-patches@sourceware.org Subject: Re: [patch] Fix disp-step-syscall.exp on some i386 targets Message-ID: <20120228134241.GA24390@host2.jankratochvil.net> References: <20120227192228.GA15792@host2.jankratochvil.net> <4F4C874E.7060203@codesourcery.com> <20120228084050.GA1296@host2.jankratochvil.net> <4F4CA669.7040209@codesourcery.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <4F4CA669.7040209@codesourcery.com> User-Agent: Mutt/1.5.21 (2010-09-15) X-IsSubscribed: yes Mailing-List: contact gdb-patches-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: gdb-patches-owner@sourceware.org X-SW-Source: 2012-02/txt/msg00651.txt.bz2 On Tue, 28 Feb 2012 11:03:21 +0100, Yao Qi wrote: > It would be better if we can add some comments to explain this fix is a hack > and why we have to do in this way. I hope it is OK this way. Thanks, Jan gdb/ 2012-02-28 Jan Kratochvil Fix disp-step-syscall.exp: fork: single step over fork. * i386-linux-tdep.c (-i386_linux_get_syscall_number): Rename to ... (i386_linux_get_syscall_number_from_regcache): ... here, new function comment, change parameters gdbarch and ptid to regcache. Remove parameter regcache, initialize gdbarch from regcache here. (i386_linux_get_syscall_number, i386_linux_displaced_step_copy_insn): New functions. (i386_linux_init_abi): Install i386_linux_displaced_step_copy_insn instead. * i386-tdep.c (i386_syscall_p): Check also for 'sysenter' and 'syscall'. Make the 'int' check more strict. gdb/testsuite/ 2012-02-28 Jan Kratochvil Fix disp-step-syscall.exp: fork: single step over fork. * gdb.base/disp-step-syscall.exp (syscall_insn): Anchor it by whitespaces. (single step over $syscall): Remove its check. (single step over $syscall final pc): New check. --- a/gdb/i386-linux-tdep.c +++ b/gdb/i386-linux-tdep.c @@ -491,11 +491,17 @@ i386_linux_record_signal (struct gdbarch *gdbarch, } +/* Core of the implementation for gdbarch get_syscall_number. Get pending + syscall number from REGCACHE. If there is no pending syscall -1 will be + returned. Pending syscall means ptrace has stepped into the syscall but + another ptrace call will step out. PC is right after the int $0x80 + / syscall / sysenter instruction in both cases, PC does not change during + the second ptrace step. */ + static LONGEST -i386_linux_get_syscall_number (struct gdbarch *gdbarch, - ptid_t ptid) +i386_linux_get_syscall_number_from_regcache (struct regcache *regcache) { - struct regcache *regcache = get_thread_regcache (ptid); + struct gdbarch *gdbarch = get_regcache_arch (regcache); enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); /* The content of a register. */ gdb_byte buf[4]; @@ -512,6 +518,18 @@ i386_linux_get_syscall_number (struct gdbarch *gdbarch, return ret; } +/* Wrapper for i386_linux_get_syscall_number_from_regcache to make it + compatible with gdbarch get_syscall_number method prototype. */ + +static LONGEST +i386_linux_get_syscall_number (struct gdbarch *gdbarch, + ptid_t ptid) +{ + struct regcache *regcache = get_thread_regcache (ptid); + + return i386_linux_get_syscall_number_from_regcache (regcache); +} + /* The register sets used in GNU/Linux ELF core-dumps are identical to the register sets in `struct user' that are used for a.out core-dumps. These are also used by ptrace(2). The corresponding @@ -643,6 +661,49 @@ i386_linux_core_read_description (struct gdbarch *gdbarch, return tdesc_i386_mmx_linux; } +/* Linux kernel shows PC value after the 'int $0x80' instruction even if + inferior is still inside the syscall. On next PTRACE_SINGLESTEP it will + finish the syscall but PC will not change. + + Some vDSOs contain 'int $0x80; ret' and during stepping out of the syscall + i386_displaced_step_fixup would keep PC at the displaced pad location. + As PC is pointing to the 'ret' instruction before the step + i386_displaced_step_fixup would expect inferior has just executed that 'ret' + and PC should not be adjusted. In reality it finished syscall instead and + PC should get relocated back to its vDSO address. Hide the 'ret' + instruction by 'nop' so that i386_displaced_step_fixup is not confused. + + It is not fully correct as the bytes in struct displaced_step_closure will + not match the inferior code. But we would need some new flag in + displaced_step_closure otherwise to keep the state that syscall is finishing + for the later i386_displaced_step_fixup execution as the syscall execution + is already no longer detectable there. The new flag field would mean + i386-linux-tdep.c needs to wrap all the displacement methods of i386-tdep.c + which does not seem worth it. The same effect is achieved by patching that + 'nop' instruction there instead. */ + +struct displaced_step_closure * +i386_linux_displaced_step_copy_insn (struct gdbarch *gdbarch, + CORE_ADDR from, CORE_ADDR to, + struct regcache *regs) +{ + struct displaced_step_closure *closure; + + closure = i386_displaced_step_copy_insn (gdbarch, from, to, regs); + + if (i386_linux_get_syscall_number_from_regcache (regs) != -1) + { + /* Since we use simple_displaced_step_copy_insn, our closure is a + copy of the instruction. */ + gdb_byte *insn = (gdb_byte *) closure; + + /* Fake nop. */ + insn[0] = 0x90; + } + + return closure; +} + static void i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { @@ -890,7 +951,7 @@ i386_linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* Displaced stepping. */ set_gdbarch_displaced_step_copy_insn (gdbarch, - i386_displaced_step_copy_insn); + i386_linux_displaced_step_copy_insn); set_gdbarch_displaced_step_fixup (gdbarch, i386_displaced_step_fixup); set_gdbarch_displaced_step_free_closure (gdbarch, simple_displaced_step_free_closure); --- a/gdb/i386-tdep.c +++ b/gdb/i386-tdep.c @@ -521,7 +521,12 @@ i386_call_p (const gdb_byte *insn) static int i386_syscall_p (const gdb_byte *insn, int *lengthp) { - if (insn[0] == 0xcd) + /* Is it 'int $0x80'? */ + if ((insn[0] == 0xcd && insn[1] == 0x80) + /* Or is it 'sysenter'? */ + || (insn[0] == 0x0f && insn[1] == 0x34) + /* Or is it 'syscall'? */ + || (insn[0] == 0x0f && insn[1] == 0x05)) { *lengthp = 2; return 1; --- a/gdb/testsuite/gdb.base/disp-step-syscall.exp +++ b/gdb/testsuite/gdb.base/disp-step-syscall.exp @@ -25,7 +25,7 @@ set syscall_insn "" # Define the syscall instruction for each target. if { [istarget "i\[34567\]86-*-linux*"] || [istarget "x86_64-*-linux*"] } { - set syscall_insn "(int|syscall|sysenter)" + set syscall_insn "\[ \t\](int|syscall|sysenter)\[ \t\]" } else { return -1 } @@ -118,7 +118,16 @@ proc disp_step_cross_syscall { syscall } { with_test_prefix "$syscall" { gdb_test_no_output "set displaced-stepping on" # Check the address of next instruction of syscall. - gdb_test "stepi" ".*$syscall_insn_next_addr.*" "single step over $syscall" + gdb_test "stepi" ".*" "single step over $syscall" + set syscall_insn_next_addr_found [get_hexadecimal_valueof "\$pc" "0"] + + set test "single step over $syscall final pc" + if {$syscall_insn_next_addr != 0 + && $syscall_insn_next_addr == $syscall_insn_next_addr_found} { + pass $test + } else { + fail $test + } # Delete breakpoint syscall insns to avoid interference to other syscalls. gdb_test_no_output "delete $syscall_insn_bp" "delete break $syscall insn"